illume avatar illume committed 865dfbd

forgot to add in some files from the scrap patch

Comments (0)

Files changed (4)

+/*
+    pygame - Python Game Library
+    Copyright (C) 2006, 2007 Rene Dudfield, Marcus von Appen
+
+    Originally written and put in the public domain by Sam Lantinga.
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+static PyObject*
+mac_scrap_call (char *name, PyObject *args)
+{
+    static PyObject *mac_scrap_module = NULL;
+    PyObject *method;
+    PyObject *result;
+
+    if (!mac_scrap_module)
+        mac_scrap_module = PyImport_ImportModule ("pygame.mac_scrap");
+    if (!mac_scrap_module)
+        return NULL;
+    
+    method = PyObject_GetAttrString (mac_scrap_module, name);
+    if (!method)
+        return NULL;
+    result = PyObject_CallObject (method, args);
+    Py_DECREF (method);
+    return result;
+}
+
+static PyObject*
+_scrap_init (PyObject *self, PyObject *args)
+{
+    return mac_scrap_call ("init", args);
+}
+
+static PyObject*
+_scrap_get_scrap (PyObject *self, PyObject *args)
+{
+    return mac_scrap_call ("get", args);
+}
+
+static PyObject*
+_scrap_put_scrap (PyObject *self, PyObject *args)
+{
+    return mac_scrap_call ("put", args);
+}
+
+static PyObject*
+_scrap_lost_scrap (PyObject *self, PyObject *args)
+{
+    return mac_scrap_call ("lost", args);
+}
+
+static PyObject*
+_scrap_get_types (PyObject *self, PyObject *args)
+{
+    return mac_scrap_call ("get_types", args);
+}
+
+static PyObject*
+_scrap_contains (PyObject *self, PyObject *args)
+{
+    return mac_scrap_call ("contains", args);
+}
+/*
+    pygame - Python Game Library
+    Copyright (C) 2006, 2007 Rene Dudfield, Marcus von Appen
+
+    Originally written and put in the public domain by Sam Lantinga.
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+static unsigned short InputGroup;
+#define MAX_CHUNK_SIZE INT_MAX
+
+/**
+ * \brief Converts the passed type into a system specific type to use
+ *        for the clipboard.
+ *
+ * \param type The type to convert.
+ * \return A system specific type.
+ */
+static uint32_t
+_convert_format (char *type)
+{
+  switch (type)
+    {
+    case PYGAME_SCRAP_TEXT:
+        return Ph_CL_TEXT;
+    default: /* PYGAME_SCRAP_BMP et al. */
+    {
+        /* TODO */
+        return 0;
+    }
+    }
+}
+
+int
+pygame_scrap_init (void)
+{
+    SDL_SysWMinfo info;
+    int retval = 0;
+
+    /* Grab the window manager specific information */
+    SDL_SetError ("SDL is not running on known window manager");
+
+    SDL_VERSION (&info.version);
+    if (SDL_GetWMInfo (&info))
+    {
+        /* Save the information for later use */
+        InputGroup = PhInputGroup (NULL);
+        retval = 1;
+    }
+    if (retval)
+        _scrapinitialized = 1;
+
+    return retval;
+}
+
+int
+pygame_scrap_lost (void)
+{
+    if (!pygame_scrap_initialized ())
+    {
+        PyErr_SetString (PyExc_SDLError, "scrap system not initialized.");
+        return 0;
+    }
+    return (PhInputGroup (NULL) != InputGroup);
+}
+
+int
+pygame_scrap_put (char *type, int srclen, char *src)
+{
+    uint32_t format;
+    int nulledlen = srclen + 1;
+
+    if (!pygame_scrap_initialized ())
+    {
+        PyErr_SetString (PyExc_SDLError, "scrap system not initialized.");
+        return 0;
+    }
+
+    format = _convert_format (type);
+
+    /* Clear old buffer and copy the new content. */
+    if (_clipbuffer)
+        free (_clipbuffer);
+
+    _clipbuffer = malloc (nulledlen);
+    if (!_clipbuffer)
+        return 0; /* Allocation failed. */
+    memset (_clipbuffer, 0, nulledlen);
+    memcpy (_clipbuffer, src, srclen);
+    _clipsize = srclen;
+    _cliptype = format;
+
+#if (_NTO_VERSION < 620) /* Before 6.2.0 releases. */
+    {
+        PhClipHeader clheader = { Ph_CLIPBOARD_TYPE_TEXT, 0, NULL };
+        int* cldata;
+        int status;
+        
+        cldata = (int *) _clipbuffer;
+        *cldata = type;
+        clheader.data = _clipbuffer;
+        if (dstlen > 65535)
+            clheader.length = 65535; /* Maximum photon clipboard size. :( */
+        else
+            clheader.length = nulledlen;
+        
+        status = PhClipboardCopy (InputGroup, 1, &clheader);
+        if (status == -1)
+        {
+            /* Could not access the clipboard, raise an error. */
+            CLEAN_CLIP_BUFFER ();
+            return 0;
+        }
+    }
+
+#else /* 6.2.0 and 6.2.1 and future releases. */
+    {
+        PhClipboardHdr clheader = { Ph_CLIPBOARD_TYPE_TEXT, 0, NULL };
+        int* cldata;
+        int status;
+        
+        cldata = (int *) _clipbuffer;
+        *cldata = type;
+        clheader.data = _clipbuffer;
+        clheader.length = nulledlen;
+
+        status = PhClipboardWrite (InputGroup, 1, &clheader);
+        if (status == -1)
+        {
+            /* Could not access the clipboard, raise an error. */
+            CLEAN_CLIP_BUFFER ();
+            return 0;
+        }
+    }
+#endif
+    return 1;
+}
+
+char*
+pygame_get_scrap (char *type)
+{
+    uint32_t format = _convert_format (type);
+    char *retval = NULL;
+
+    if (!pygame_scrap_initialized ())
+    {
+        PyErr_SetString (PyExc_SDLError, "scrap system not initialized.");
+        return 0;
+    }
+
+    /* If we are the owner, simply return the clip buffer, if it matches
+     * the request type. */
+    if (!pygame_scrap_lost ())
+    {
+        if (format != _cliptype)
+            return NULL;
+
+        if (_clipbuffer)
+        {
+            retval = malloc (_clipsize + 1);
+            if (!retval)
+                return NULL;
+            memset (retval, 0, _clipsize + 1);
+            memcpy (retval, _clipbuffer, _clipsize + 1);
+            return retval;
+        }
+        return NULL;
+    }
+
+#if (_NTO_VERSION < 620) /* before 6.2.0 releases */
+    {
+        void *clhandle;
+        PhClipHeader *clheader;
+        int *cldata;
+        
+        clhandle = PhClipboardPasteStart (InputGroup);
+        
+        if (clhandle)
+        {
+            clheader = PhClipboardPasteType (clhandle, Ph_CLIPBOARD_TYPE_TEXT);
+            if (clheader)
+            {
+                cldata = clheader->data;
+                if (*cldata == type)
+                    retval = malloc (clheader->length + 1);
+                
+                if (retval)
+                {
+                    memset (retval, 0, clheader->length + 1);
+                    memcpy (retval, cldata, clheader->length + 1);
+                }
+            }
+            PhClipboardPasteFinish (clhandle);
+        }
+    }
+#else /* 6.2.0 and 6.2.1 and future releases */
+    {
+        void* clhandle;
+        PhClipboardHdr* clheader;
+        int* cldata;
+        
+        clheader = PhClipboardRead (InputGroup, Ph_CLIPBOARD_TYPE_TEXT);
+        if (clheader)
+        {
+            cldata = clheader->data;
+            if (*cldata == type)
+                retval = malloc (clheader->length + 1);
+            
+            if (retval)
+            {
+                memset (retval, 0, clheader->length + 1);
+                memcpy (retval, cldata, clheader->length + 1);
+            }
+            /* According to the QNX 6.x docs, the clheader pointer is a
+             * newly created one that must be freed manually. */
+            free (clheader->data);
+            free (clheader);
+        }
+    }
+#endif
+
+    return retval;
+}
+
+
+char**
+pygame_scrap_get_types (void)
+{
+    return NULL;
+}
+
+int
+pygame_scrap_contains (char *type)
+{
+    return 0;
+}
+/*
+    pygame - Python Game Library
+    Copyright (C) 2006, 2007 Rene Dudfield, Marcus von Appen
+
+    Originally written and put in the public domain by Sam Lantinga.
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <Windows.h>
+
+static HWND SDL_Window;
+#define MAX_CHUNK_SIZE INT_MAX
+
+static UINT _format_TEXT;
+
+/**
+ * \brief Converts the passed type into a system specific type to use
+ *        for the clipboard.
+ *
+ * \param type The type to convert.
+ * \return A system specific type.
+ */
+static UINT
+_convert_format (char *type)
+{
+    return RegisterClipboardFormat (type);
+}
+
+int
+pygame_scrap_init (void)
+{
+    SDL_SysWMinfo info;
+    int retval = 0;
+
+    /* Grab the window manager specific information */
+    SDL_SetError ("SDL is not running on known window manager");
+
+    SDL_VERSION (&info.version);
+    if (SDL_GetWMInfo (&info))
+    {
+        /* Save the information for later use */
+        SDL_Window = info.window;
+        retval = 1;
+    }
+    if (retval)
+        _scrapinitialized = 1;
+    
+    _format_MIME_PLAIN = RegisterClipboardFormat ("text/plain");
+    return retval;
+}
+
+int
+pygame_scrap_lost (void)
+{
+    if (!pygame_scrap_initialized ())
+    {
+        PyErr_SetString (PyExc_SDLError, "scrap system not initialized.");
+        return 0;
+    }
+    return (GetClipboardOwner () != SDL_Window);
+}
+
+int
+pygame_scrap_put (char *type, int srclen, char *src)
+{
+    UINT format;
+    int nulledlen = srclen + 1;
+    HANDLE hMem;
+
+    if (!pygame_scrap_initialized ())
+    {
+        PyErr_SetString (PyExc_SDLError, "scrap system not initialized.");
+        return 0;
+    }
+
+    format = _convert_format (type);
+
+    if (!OpenClipboard (SDL_Window))
+        return 0; /* Could not open the clipboard. */
+    
+    hMem = GlobalAlloc ((GMEM_MOVEABLE | GMEM_DDESHARE), nulledlen);
+    if (hMem)
+    {
+        char *dst = GlobalLock (hMem);
+
+        memset (dst, 0, nulledlen);
+        memcpy (dst, src, srclen);
+
+        GlobalUnlock (hMem);
+        EmptyClipboard ();
+        SetClipboardData (format, hMem);
+        
+        if (format == _format_MIME_PLAIN) 
+        {
+            /* Setting SCRAP_TEXT, also set CF_TEXT. */
+            SetClipboardData (CF_TEXT, hMem);
+            PyDict_SetItemString (_clipdata, "TEXT", PyString_FromString (src));
+        }
+        CloseClipboard ();
+    }
+    else
+    {
+        /* Could not access the clipboard, raise an error. */
+        CloseClipboard ();
+        return 0;
+    }
+
+    return 1;
+}
+
+char*
+pygame_scrap_get (char *type)
+{
+    UINT format = _convert_format (type);
+    char *retval = NULL;
+
+    if (!pygame_scrap_initialized ())
+    {
+        PyErr_SetString (PyExc_SDLError, "scrap system not initialized.");
+        return 0;
+    }
+
+    if (!pygame_scrap_lost ())
+        return PyString_AsString (PyDict_GetItemString (_clipdata, type));
+
+    if (IsClipboardFormatAvailable (format) && OpenClipboard (SDL_Window))
+    {
+        HANDLE hMem;
+        char *src;
+        
+        hMem = GetClipboardData (format);
+        if (hMem)
+        {
+            int len = 0;
+            
+            /* TODO: Is there any mechanism to detect the amount of bytes
+             * in the HANDLE? strlen() won't work as supposed, if the
+             * sequence contains NUL bytes. Can this even happen in the 
+             * Win32 clipboard or is NUL the usual delimiter?
+             */
+            src = GlobalLock (hMem);
+            len = strlen (src) + 1;
+            
+            retval = malloc (len);
+            if (retval)
+            {
+                memset (retval, 0, len);
+                memcpy (retval, src, len);
+            }
+            GlobalUnlock (hMem);
+        }
+        CloseClipboard ();
+    }
+    
+    return retval;
+}
+
+char**
+pygame_scrap_get_types (void)
+{
+    UINT format = 0;
+    char **types;
+    int i = 0;
+    int len;
+    char tmp[100] = { NULL };
+    int size =  CountClipboardFormats ();
+
+    if (size == 0)
+        return NULL; /* No clipboard data. */
+
+    types = malloc (sizeof (char *) * (size + 1));
+    for (i = 0; i < size; i++)
+    {
+        format = EnumClipboardFormats (format);
+        if (format == 0)
+        {
+            /* Something wicked happened. */
+            while (i > 0)
+                free (types[i]);
+            free (types);
+            return NULL;
+            break;
+        }
+
+        /* No predefined name, get the (truncated) name. */
+        memset (tmp, 0, sizeof (tmp));
+        len = GetClipboardFormatName (format, tmp, sizeof (tmp));
+        types[i] = malloc (sizeof (char) * (len + 1));
+        if (!types[i])
+        {
+            while (i > 0)
+                free (types[i]);
+            free (types);
+            return NULL;
+        }
+        memset (types[i], 0, len + 1);
+        memcpy (types[i], tmp, len);
+    }
+    types[size] = NULL;
+    return NULL;
+}
+
+int
+pygame_scrap_contains (char *type)
+{
+    return IsClipboardFormatAvailable (format);
+}
+/*
+    pygame - Python Game Library
+    Copyright (C) 2006, 2007 Rene Dudfield, Marcus von Appen
+
+    Originally written and put in the public domain by Sam Lantinga.
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <X11/Xutil.h>
+
+static Display *SDL_Display;
+static Window SDL_Window;
+static void (*Lock_Display)(void);
+static void (*Unlock_Display)(void);
+
+/* Atoms used. */
+static Atom _atom_UTF8;
+static Atom _atom_TEXT;
+static Atom _atom_COMPOUND;
+static Atom _atom_MIME_PLAIN;
+static Atom _atom_MIME_UTF8;
+static Atom _atom_MIME_LOCALE;
+static Atom _atom_TARGETS;
+static Atom _atom_TIMESTAMP;
+static Atom _atom_SDL;
+static Atom _atom_BMP;
+static Atom _atom_CLIPBOARD;
+
+/* Timestamps for the requests. */
+static Time _cliptime = CurrentTime;
+static Time _selectiontime = CurrentTime;
+
+/* Maximum size to send or receive per request. */
+#define MAX_CHUNK_SIZE(display)                                    \
+    MIN(262144, /* 65536 * 4 */                                    \
+        (XExtendedMaxRequestSize (display)) == 0                   \
+        ? XMaxRequestSize (display) - 100                          \
+        : XExtendedMaxRequestSize (display) - 100)
+
+#define GET_CLIPATOM(x) ((x == SCRAP_SELECTION) ? XA_PRIMARY : _atom_CLIPBOARD)
+
+static Atom _convert_format (char *type);
+static void _init_atom_types (void);
+static char* _atom_to_string (Atom a);
+static void _add_clip_data (Atom type, char *data, int srclen);
+static int _clipboard_filter (const SDL_Event *event);
+static void _set_targets (PyObject *data, Display *display, Window window,
+                          Atom property);
+static int _set_data (PyObject *dict, Display *display, Window window,
+                      Atom property, Atom target);
+static Window _get_scrap_owner (Atom *selection);
+static char* _get_data_as (Atom source, Atom format, unsigned long *length);
+
+/**
+ * \brief Converts the passed type into a system specific type to use
+ *        for the clipboard.
+ *
+ * \param type The type to convert.
+ * \return A system specific type.
+ */
+static Atom
+_convert_format (char *type)
+{
+    if (strcmp (type, PYGAME_SCRAP_PPM) == 0)
+        return XA_PIXMAP;
+    if (strcmp (type, PYGAME_SCRAP_PBM) == 0)
+        return XA_BITMAP;
+    return XInternAtom (SDL_Display, type, False);
+}
+
+/**
+ * \brief Initializes the used atom types.
+ */
+static void
+_init_atom_types (void)
+{
+    _atom_UTF8 = XInternAtom (SDL_Display, "UTF8_STRING", False);
+    _atom_TEXT = XInternAtom (SDL_Display, "TEXT", False);
+    _atom_COMPOUND = XInternAtom (SDL_Display, "COMPOUND_TEXT", False);
+    _atom_MIME_PLAIN = XInternAtom (SDL_Display, "text/plain", False);
+    _atom_MIME_UTF8  = XInternAtom (SDL_Display, "text/plain;charset=utf-8",
+                                    False);
+    _atom_MIME_LOCALE = XInternAtom (SDL_Display, "text/plain;charset=%s",
+                                     False);
+    _atom_TARGETS = XInternAtom (SDL_Display, "TARGETS", False);
+    _atom_TIMESTAMP = XInternAtom (SDL_Display, "TIMESTAMP", False);
+    _atom_SDL = XInternAtom (SDL_Display, "SDL_SELECTION", False);
+    _atom_BMP = XInternAtom (SDL_Display, PYGAME_SCRAP_BMP, False);
+    _atom_CLIPBOARD = XInternAtom (SDL_Display, "CLIPBOARD", False);
+}
+
+/**
+ * \brief Returns the name of the passed Atom. The result has to be
+ * freed by the caller using free().
+ *
+ * \param a The Atom to get the name for.
+ * \return The name of the Atom.
+ */
+static char*
+_atom_to_string (Atom a)
+{
+    char *name;
+    char *retval;
+
+    if (!a)
+        return NULL;
+    name = XGetAtomName (SDL_Display, a);
+    retval = strdup (name);
+    XFree (name);
+    return retval;
+}
+
+/**
+ * Adds additional data to the currently selected clipboard and Window
+ * if it does not already exist.
+ *
+ * \param cliptype The Atom to set the data for.
+ * \param data The data to set.
+ * \param srclen The length of the data.
+ */
+static void
+_add_clip_data (Atom cliptype, char *data, int srclen)
+{
+    Atom clip = GET_CLIPATOM(_currentmode);
+    PyObject* dict = (_currentmode == SCRAP_CLIPBOARD) ? _clipdata :
+        _selectiondata;
+    char *key = _atom_to_string (cliptype);
+
+    PyDict_SetItemString (dict, key, PyString_FromStringAndSize (data, srclen));
+    XChangeProperty (SDL_Display, SDL_Window, clip, cliptype,
+                     8, PropModeReplace, (unsigned char *) data, srclen);
+    free (key);
+}
+
+/**
+ * \brief System message filter function -- handles X11 clipboard messages.
+ *
+ * \param event The SDL_Event to check.
+ * \return Always 1.
+ */
+static int
+_clipboard_filter (const SDL_Event *event)
+{
+    PyObject *dict = NULL;
+    Time timestamp;
+
+    /* Post all non-window manager specific events */
+    if (event->type != SDL_SYSWMEVENT)
+        return 1;
+
+    XEvent xevent = event->syswm.msg->event.xevent;
+
+    /* Handle window-manager specific clipboard events */
+    switch (xevent.type)
+    {
+    case PropertyNotify:
+    {
+        /* Handled in pygame_scrap_put(). */
+        break;
+    }
+    case SelectionClear:
+    {
+        XSelectionClearEvent *clear = &xevent.xselectionclear;
+
+        /* Looks like another window takes control over the clipboard.
+         * Release the internally saved buffer, if any.
+         */
+        if (clear->selection == XA_PRIMARY)
+            timestamp = _selectiontime;
+        else if(clear->selection == _atom_CLIPBOARD)
+            timestamp = _cliptime;
+        else
+            break;
+
+        /* Do not do anything, if the times do not match. */
+        if (timestamp != CurrentTime && xevent.xselectionclear.time < timestamp)
+            break;
+
+        /* Clean the dictionaries. */
+        if (clear->selection == XA_PRIMARY)
+            PyDict_Clear (_selectiondata);
+        else if (clear->selection != _atom_CLIPBOARD)
+            PyDict_Clear (_clipdata);
+        break;
+    }
+    case SelectionNotify:
+        /* This one will be handled directly in the pygame_scrap_get ()
+         * function.
+         */
+        break;
+
+    case SelectionRequest:
+    {
+        XSelectionRequestEvent *req = &xevent.xselectionrequest;
+        XEvent ev;
+        
+        /* Prepare answer. */
+        ev.xselection.type      = SelectionNotify;
+        ev.xselection.display   = req->display;
+        ev.xselection.requestor = req->requestor;
+        ev.xselection.selection = req->selection;
+        ev.xselection.target    = req->target;
+        ev.xselection.property  = None;
+        ev.xselection.time      = req->time;
+
+        /* Which clipboard type was requested? */
+        if (req->selection == XA_PRIMARY)
+        {
+            dict = _selectiondata;
+            timestamp = _selectiontime;
+        }
+        else if (req->selection == _atom_CLIPBOARD)
+        {
+            dict = _clipdata;
+            timestamp = _cliptime;
+
+        }
+        else /* Anything else's not supported. */
+        {
+            XSendEvent (req->display, req->requestor, False, NoEventMask, &ev);
+            return 1;
+        }
+
+        /* No data? */
+        if (PyDict_Size (dict) == 0)
+        {
+            XSendEvent (req->display, req->requestor, False, NoEventMask, &ev);
+            return 1;
+        }
+
+        /* We do not own the selection anymore. */
+        if (timestamp == CurrentTime
+            || (req->time != CurrentTime && timestamp > req->time))
+        {
+            XSendEvent (req->display, req->requestor, False, NoEventMask, &ev);
+            return 1;
+        }
+
+        /* 
+         * TODO: We have to make it ICCCM compatible at some point by
+         * implementing the MULTIPLE atom request.
+         */
+
+        /* Old client? */
+        if (req->property == None)
+            ev.xselection.property = req->target;
+        
+        if (req->target == _atom_TARGETS)
+        {
+            /* The requestor wants to know, what we've got. */
+            _set_targets (dict, req->display, req->requestor, req->property);
+        }
+        else
+        {
+            _set_data (dict, req->display, req->requestor, req->property,
+                       req->target);
+        }
+
+        ev.xselection.property = req->property;
+        /* Post the event for X11 clipboard reading above */
+        XSendEvent (req->display, req->requestor, False, 0, &ev);
+        break;
+    }
+    }
+    return 1;
+}
+
+/**
+ * Sets the list of target atoms available in the clipboard.
+ *
+ * \param data The clipboard dictionary.
+ * \param display The requesting Display.
+ * \param window The requesting Window.
+ * \param property The request property to place the list into.
+ */
+static void
+_set_targets (PyObject *data, Display *display, Window window, Atom property)
+{
+    int i;
+    char *format;
+    PyObject *list = PyDict_Keys (data);
+    int amount = PyList_Size (list);
+    /* All types plus the TARGETS and a TIMESTAMP atom. */
+    Atom *targets = malloc ((amount + 2) * sizeof (Atom));
+    if (targets == NULL)
+        return;
+    memset (targets, 0, (amount + 2) * sizeof (Atom));
+    targets[0] = _atom_TARGETS;
+    targets[1] = _atom_TIMESTAMP;
+    for (i = 0; i < amount; i++)
+    {
+        format = PyString_AsString (PyList_GetItem(list, i));
+        targets[i + 2] = _convert_format (format);
+    }
+    XChangeProperty (display, window, property, XA_ATOM, 32, PropModeReplace,
+                     (unsigned char*) targets, amount);
+}
+
+/**
+ * Places the requested Atom data into a Window.
+ *
+ * \param data The clipboard dictionary.
+ * \param display The requesting Display.
+ * \param window The requesting Window.
+ * \param property The request property to place the list into.
+ * \param target The target porperty to place the list into.
+ * \return 0 if no data for the target is available, 1 on success.
+ */
+static int
+_set_data (PyObject *data, Display *display, Window window, Atom property,
+           Atom target)
+{
+    char *name = _atom_to_string (target);
+    PyObject *val = PyDict_GetItemString (data, name);
+    char *value = NULL;
+    int size;
+
+    if (!val)
+    {
+        XFree (name);
+        return 0;
+    }
+    size = PyString_Size (val);
+    value = PyString_AsString (val);
+
+    /* Send data. */
+    XChangeProperty (display, window, property, target, 8, PropModeReplace,
+                     (unsigned char *) value, size);
+    XFree (name);
+    return 1;
+}
+
+/**
+ * \brief Tries to determine the X window with a valid selection.
+ *        Default is to check
+ *         - passed parameter
+ *         - CLIPBOARD
+ *         - XA_PRIMARY
+ *         - XA_SECONDARY
+ *         - XA_CUT_BUFFER0 - 7
+ *        
+ *         in this order.
+ * 
+ * \param selection The Atom type, that should be tried before any of the
+ *                  fixed XA_* buffers.
+ * \return The Window handle, that owns the selection or None if none was
+ *         found.
+ */
+static Window
+_get_scrap_owner (Atom *selection)
+{
+    int i = 0;
+    static Atom buffers[] = { XA_PRIMARY, XA_SECONDARY, XA_CUT_BUFFER0,
+                              XA_CUT_BUFFER1, XA_CUT_BUFFER2, XA_CUT_BUFFER3,
+                              XA_CUT_BUFFER4, XA_CUT_BUFFER5, XA_CUT_BUFFER6,
+                              XA_CUT_BUFFER7 };
+
+    Window owner = XGetSelectionOwner (SDL_Display, *selection);
+    if (owner != None)
+        return owner;
+
+    owner = XGetSelectionOwner (SDL_Display, _atom_CLIPBOARD);
+    if (owner != None)
+        return owner;
+
+    while (i < 10)
+    {
+        owner = XGetSelectionOwner (SDL_Display, buffers[i]);
+        if (owner != None)
+        {
+            *selection = buffers[i];
+            return owner;
+        }
+        i++;
+    }
+    
+    return None;
+}
+
+/**
+ * Retrieves the data from a certain source Atom using a specific
+ * format.
+ *
+ * \param source The currently active clipboard Atom to get the data from.
+ * \param format The format of the data to get.
+ * \param length Out parameter that contains the length of the returned
+ * buffer.
+ * \return The requested content or NULL in case no content exists or an
+ * error occured.
+ */
+static char*
+_get_data_as (Atom source, Atom format, unsigned long *length)
+{
+    char *retval = NULL;
+    Window owner;
+    time_t start;
+    Atom sel_type;
+    int sel_format;
+    unsigned long nbytes;
+    unsigned long overflow;
+    *length = 0;
+    unsigned char *src;
+    unsigned long offset = 0;
+    unsigned long chunk = 0;
+    int step = 1;
+    XEvent ev;
+    Time timestamp;
+
+    /* If we are the owner, simply return the clip buffer, if it matches
+     * the request type.
+     */
+    if (!pygame_scrap_lost ())
+    {
+        char *fmt;
+        char *data;
+
+        fmt = _atom_to_string (format);
+
+        if (_currentmode == SCRAP_SELECTION)
+            data = PyString_AsString
+                (PyDict_GetItemString (_selectiondata, fmt));
+        else
+            data = PyString_AsString (PyDict_GetItemString (_clipdata, fmt));
+        free (fmt);
+
+        return data;
+    }
+
+    Lock_Display ();
+    
+    /* Find a selection owner. */
+    owner = _get_scrap_owner (&source);
+    if (owner == None)
+    {
+        Unlock_Display ();
+        return NULL;
+    }
+
+    timestamp = (source == XA_PRIMARY) ?  _selectiontime : _cliptime;
+
+    /* Copy and convert the selection into our SDL_SELECTION atom of the
+     * window. 
+     * Flush afterwards, so we have an immediate effect and do not receive
+     * the old buffer anymore.
+     */
+    XConvertSelection (SDL_Display, source, format, _atom_SDL, SDL_Window,
+                       timestamp);
+    XSync (SDL_Display, False);
+
+    /* Let's wait for the SelectionNotify event from the callee and
+     * react upon it as soon as it is received.
+     */
+    for (start = time (0);;)
+    {
+        if (XCheckTypedWindowEvent (SDL_Display, SDL_Window,
+                                    SelectionNotify, &ev))
+            break;
+        if (time (0) - start >= 5)
+        {
+            /* Timeout, damn. */
+            Unlock_Display ();
+            return NULL;
+        }
+    }
+
+    /* Get any property type and check the sel_type afterwards to decide
+     * what to do.
+     */
+    if (XGetWindowProperty (SDL_Display, ev.xselection.requestor,
+                            _atom_SDL, 0, 0, True,
+                            AnyPropertyType, &sel_type, &sel_format,
+                            &nbytes, &overflow, &src) != Success)
+    {
+        XFree (src);
+        Unlock_Display ();
+        return NULL;
+    }
+
+    /* In case we requested a SCRAP_TEXT, any property type of
+     * XA_STRING, XA_COMPOUND_TEXT, UTF8_STRING and TEXT is valid.
+     */
+    if (format == _atom_MIME_PLAIN &&
+        (sel_type != _atom_UTF8 && sel_type != _atom_TEXT
+         && sel_type != _atom_COMPOUND && sel_type != XA_STRING))
+    {
+        /* No matching text type found. Return nothing then. */
+        XFree (src);
+        Unlock_Display ();
+        return NULL;
+    }
+
+    /* Anything is fine, so copy the buffer and return it. */
+    switch (sel_format)
+    {
+    case 16:
+        step = sizeof (short) / 2;
+        break;
+    case 32:
+        step = sizeof (long) / 4;
+        break;
+    case 8:
+    default:
+        step = sizeof (char);
+        *length = overflow; /* 8 bit size is already correctly set in nbytes.*/
+        break;
+    }
+
+    /* X11 guarantees NULL termination, add an extra byte. */
+    *length = step * overflow;
+    retval = malloc (*length + 1);
+    if (retval)
+    {
+        unsigned long boffset = 0;
+        chunk = MAX_CHUNK_SIZE(SDL_Display);
+        memset (retval, 0, *length + 1);
+
+        /* Read as long as there is data. */
+        while (overflow)
+        {
+            if (XGetWindowProperty (SDL_Display, ev.xselection.requestor,
+                                    _atom_SDL, offset, chunk, True,
+                                    AnyPropertyType, &sel_type, &sel_format,
+                                    &nbytes, &overflow, &src) != Success)
+            {
+                break;
+            }
+            
+            offset += nbytes / (32 / sel_format);
+            nbytes *= step * sel_format / 8;
+            memcpy (retval + boffset, src, nbytes);
+            boffset += nbytes;
+            XFree (src);
+        }
+    }
+    else
+    {
+        /* ENOMEM */
+        return NULL;
+    }
+
+    /* In case we've got a COMPOUND_TEXT, convert it to the current
+     * multibyte locale.
+     */
+    if (sel_type == _atom_COMPOUND && sel_format == 8)
+    {
+        char **list = NULL;
+        int count;
+        int status = 0;
+        XTextProperty p;
+
+        p.encoding = sel_type;
+        p.format = sel_format;
+        p.nitems = nbytes;
+        p.value = retval;
+
+        status = XmbTextPropertyToTextList (SDL_Display, &p, &list, &count);
+        if (status == XLocaleNotSupported || status == XConverterNotFound)
+        {
+            free (retval);
+            PyErr_SetString (PyExc_SDLError,
+                             "current locale is not supported for conversion.");
+            return NULL;
+        }
+        else if (status == XNoMemory)
+        {
+            free (retval);
+            return NULL;
+        }
+        else if (status == Success)
+        {
+            if (count && list)
+            {
+                int i = 0;
+                int offset = 0;
+                int length = 0;
+                char *tmp;
+
+                free (retval);
+                retval = NULL;
+                for (i = 0; i < count; i++)
+                {
+                    length = strlen (list[i]);
+                    tmp = retval;
+                    retval = realloc (retval, length + 1);
+                    if (!retval)
+                    {
+                        free (tmp);
+                        return NULL;
+                    }
+                    offset += length;
+
+                    memcpy (retval, list[i], length);
+                    memset (retval + offset, '\n', 1);
+                }
+                memset (retval + offset, 0, 1);
+            }
+        }
+
+        if (list)
+            XFreeStringList (list);
+    }
+
+    Unlock_Display ();
+    return retval;
+}
+
+int
+pygame_scrap_init (void)
+{
+    SDL_SysWMinfo info;
+    int retval = 0;
+
+    /* Grab the window manager specific information */
+    SDL_SetError ("SDL is not running on known window manager");
+
+    SDL_VERSION (&info.version);
+    if (SDL_GetWMInfo (&info))
+    {
+        /* Save the information for later use */
+        if (info.subsystem == SDL_SYSWM_X11)
+        {
+            XWindowAttributes setattrs;
+            XSetWindowAttributes newattrs;
+
+            newattrs.event_mask = PropertyChangeMask;
+
+            SDL_Display = info.info.x11.display;
+            SDL_Window = info.info.x11.window;
+            Lock_Display = info.info.x11.lock_func;
+            Unlock_Display = info.info.x11.unlock_func;
+            
+            Lock_Display ();
+
+            /* We need the PropertyNotify event for the timestap, so
+             * modify the event attributes.
+             */
+            XGetWindowAttributes (SDL_Display, SDL_Window, &setattrs);
+            newattrs.event_mask |= setattrs.all_event_masks;
+            XChangeWindowAttributes
+                (SDL_Display, SDL_Window, CWEventMask, &newattrs);
+
+            Unlock_Display ();
+
+            /* Enable the special window hook events */
+            SDL_EventState (SDL_SYSWMEVENT, SDL_ENABLE);
+            SDL_SetEventFilter (_clipboard_filter);
+
+            /* Create the atom types we need. */
+            _init_atom_types ();
+
+            retval = 1;
+        }
+        else
+            SDL_SetError ("SDL is not running on X11");
+    }
+    if (retval)
+        _scrapinitialized = 1;
+
+    return retval;
+}
+
+int
+pygame_scrap_lost (void)
+{
+    int retval;
+
+    if (!pygame_scrap_initialized ())
+    {
+        PyErr_SetString (PyExc_SDLError, "scrap system not initialized.");
+        return 0;
+    }
+
+    Lock_Display ();
+    retval = (XGetSelectionOwner (SDL_Display, GET_CLIPATOM (_currentmode))
+              != SDL_Window);
+    Unlock_Display ();
+
+    return retval;
+}
+
+int
+pygame_scrap_put (char *type, int srclen, char *src)
+{
+    Atom clip;
+    Atom cliptype;
+    Time timestamp;
+    time_t start;
+    XEvent ev;
+
+    if (!pygame_scrap_initialized ())
+    {
+        PyErr_SetString (PyExc_SDLError, "scrap system not initialized.");
+        return 0;
+    }
+
+    Lock_Display ();
+
+    clip = GET_CLIPATOM (_currentmode);
+    cliptype = _convert_format (type);
+
+    /* We've some types which should not be set by the user. */
+    if (cliptype == _atom_TARGETS || cliptype == _atom_SDL
+        || cliptype == _atom_TIMESTAMP)
+    {
+        PyErr_SetString (PyExc_ValueError, "the requested type is reserved.");
+        Unlock_Display ();
+        return 0;
+    }
+
+    /* Update the clipboard property with the buffer. */
+    XChangeProperty (SDL_Display, SDL_Window, clip, cliptype,
+                     8, PropModeReplace, (unsigned char *) src, srclen);
+
+    if (cliptype == _atom_MIME_PLAIN)
+    {
+        /* Set PYGAME_SCRAP_TEXT. Also set XA_STRING, TEXT and
+         * UTF8_STRING if they are not set in the dictionary.
+         */
+        _add_clip_data (XA_STRING, src, srclen);
+        _add_clip_data (_atom_UTF8, src, srclen);
+        _add_clip_data (_atom_TEXT, src, srclen);
+    }
+    XSync (SDL_Display, False);
+
+    /* Update the timestamp */
+    for (start = time (0);;)
+    {
+        if (XCheckTypedWindowEvent (SDL_Display, SDL_Window,
+                                    PropertyNotify, &ev))
+            break;
+        if (time (0) - start >= 5)
+        {
+            /* Timeout, damn. */
+            Unlock_Display ();
+            goto SETSELECTIONOWNER;
+        }
+    }
+    if (ev.xproperty.atom == clip)
+    {
+        timestamp = ev.xproperty.time;
+
+        if (cliptype == XA_PRIMARY)
+            _selectiontime = timestamp;
+        else
+            _cliptime = timestamp;
+    }
+    else
+        timestamp = (cliptype == XA_PRIMARY) ? _selectiontime : _cliptime;
+
+SETSELECTIONOWNER:
+    /* Set the selection owner to the own window. */
+    XSetSelectionOwner (SDL_Display, clip, SDL_Window, timestamp);
+    if (XGetSelectionOwner (SDL_Display, clip) != SDL_Window)
+    {
+        /* Ouch, we could not toggle the selection owner. Raise an
+         * error, as it's not guaranteed, that the clipboard
+         * contains valid data.
+         */
+        Unlock_Display ();
+        return 0;
+    }
+
+    Unlock_Display ();
+    return 1;
+}
+
+char*
+pygame_scrap_get (char *type, unsigned long *count)
+{
+    if (!pygame_scrap_initialized ())
+    {
+        PyErr_SetString (PyExc_SDLError, "scrap system not initialized.");
+        return NULL;
+    }
+    return _get_data_as (GET_CLIPATOM (_currentmode),
+                         _convert_format (type), count);
+}
+
+int
+pygame_scrap_contains (char *type)
+{
+    int i = 0;
+    char **types = pygame_scrap_get_types ();
+    while (types[i])
+    {
+        if (strcmp (type, types[i]) == 0)
+            return 1;
+        i++;
+    }
+    return 0;
+}
+
+char**
+pygame_scrap_get_types (void)
+{
+    char **types;
+    Atom *targetdata;
+    unsigned long length;
+    
+    if (!pygame_scrap_lost ())
+    {
+        PyObject *key;
+        int pos = 0;
+        int i = 0;
+        PyObject *dict = (_currentmode == SCRAP_SELECTION) ? _selectiondata :
+            _clipdata;
+       
+        types = malloc (sizeof (char*) * (PyDict_Size (dict) + 1));
+        if (!types)
+            return NULL;
+
+        memset (types, 0, (PyDict_Size (dict) + 1));
+        while (PyDict_Next (dict, &pos, &key, NULL))
+        {
+            types[i] = strdup (PyString_AsString (key));
+            if (!types[i])
+            {
+                /* Could not allocate memory, free anything. */
+                int j = 0;
+                while (types[j])
+                {
+                    free (types[j]);
+                    j++;
+                }
+                free (types);
+                return NULL;
+            }
+            i++;
+        }
+        return types;
+    }
+    
+    targetdata = (Atom *) _get_data_as (GET_CLIPATOM (_currentmode),
+                                        _atom_TARGETS, &length);
+    if (length > 0 && targetdata != NULL)
+    {
+        Atom *data =  targetdata;
+        int count = length / sizeof (Atom);
+        int i;
+        char **targets  = malloc (sizeof (char *) * (count + 1));
+
+        if (targets == NULL)
+        {
+            free (targetdata);
+            return NULL;
+        }
+        memset (targets, 0, sizeof (char *) * (count + 1));
+
+        for (i = 0; i < count; i++)
+            targets[i] = _atom_to_string (data[i]);
+
+        free (targetdata);
+        return targets;
+   }
+    return NULL;
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.