Skip to content
Snippets Groups Projects
juce_linux_Windowing.cpp 122 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
      ==============================================================================
    
       This file is part of the JUCE library - "Jules' Utility Class Extensions"
    
    jsiegle's avatar
    jsiegle committed
       Copyright 2004-11 by Raw Material Software Ltd.
    
    
      ------------------------------------------------------------------------------
    
       JUCE can be redistributed and/or modified under the terms of the GNU General
       Public License (Version 2), as published by the Free Software Foundation.
       A copy of the license is included in the JUCE distribution, or can be found
       online at www.gnu.org/licenses.
    
       JUCE 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 General Public License for more details.
    
      ------------------------------------------------------------------------------
    
       To release a closed-source product which uses JUCE, commercial licenses are
       available: visit www.rawmaterialsoftware.com/juce for more information.
    
      ==============================================================================
    */
    
    extern Display* display;
    extern XContext windowHandleXContext;
    
    jsiegle's avatar
    jsiegle committed
    typedef void (*WindowMessageReceiveCallback) (XEvent&);
    extern WindowMessageReceiveCallback dispatchWindowMessage;
    
    
    
    //==============================================================================
    
    jsiegle's avatar
    jsiegle committed
    struct Atoms
    
    jsiegle's avatar
    jsiegle committed
        Atoms()
        {
            Protocols                       = getIfExists ("WM_PROTOCOLS");
            ProtocolList [TAKE_FOCUS]       = getIfExists ("WM_TAKE_FOCUS");
            ProtocolList [DELETE_WINDOW]    = getIfExists ("WM_DELETE_WINDOW");
            ProtocolList [PING]             = getIfExists ("_NET_WM_PING");
            ChangeState                     = getIfExists ("WM_CHANGE_STATE");
            State                           = getIfExists ("WM_STATE");
            UserTime                        = getCreating ("_NET_WM_USER_TIME");
            ActiveWin                       = getCreating ("_NET_ACTIVE_WINDOW");
            Pid                             = getCreating ("_NET_WM_PID");
            WindowType                      = getIfExists ("_NET_WM_WINDOW_TYPE");
            WindowState                     = getIfExists ("_NET_WM_STATE");
    
            XdndAware                       = getCreating ("XdndAware");
            XdndEnter                       = getCreating ("XdndEnter");
            XdndLeave                       = getCreating ("XdndLeave");
            XdndPosition                    = getCreating ("XdndPosition");
            XdndStatus                      = getCreating ("XdndStatus");
            XdndDrop                        = getCreating ("XdndDrop");
            XdndFinished                    = getCreating ("XdndFinished");
            XdndSelection                   = getCreating ("XdndSelection");
    
            XdndTypeList                    = getCreating ("XdndTypeList");
            XdndActionList                  = getCreating ("XdndActionList");
            XdndActionCopy                  = getCreating ("XdndActionCopy");
            XdndActionPrivate               = getCreating ("XdndActionPrivate");
            XdndActionDescription           = getCreating ("XdndActionDescription");
    
            allowedMimeTypes[0]             = getCreating ("UTF8_STRING");
            allowedMimeTypes[1]             = getCreating ("text/plain;charset=utf-8");
            allowedMimeTypes[2]             = getCreating ("text/plain");
            allowedMimeTypes[3]             = getCreating ("text/uri-list");
    
            externalAllowedFileMimeTypes[0] = getCreating ("text/uri-list");
            externalAllowedTextMimeTypes[0] = getCreating ("text/plain");
    
            allowedActions[0]               = getCreating ("XdndActionMove");
            allowedActions[1]               = XdndActionCopy;
            allowedActions[2]               = getCreating ("XdndActionLink");
            allowedActions[3]               = getCreating ("XdndActionAsk");
            allowedActions[4]               = XdndActionPrivate;
        }
    
        static const Atoms& get()
        {
            static Atoms atoms;
            return atoms;
        }
    
    
        enum ProtocolItems
        {
            TAKE_FOCUS = 0,
            DELETE_WINDOW = 1,
            PING = 2
        };
    
    
    jsiegle's avatar
    jsiegle committed
        Atom Protocols, ProtocolList[3], ChangeState, State, UserTime,
             ActiveWin, Pid, WindowType, WindowState,
             XdndAware, XdndEnter, XdndLeave, XdndPosition, XdndStatus,
             XdndDrop, XdndFinished, XdndSelection, XdndTypeList, XdndActionList,
             XdndActionDescription, XdndActionCopy, XdndActionPrivate,
             allowedActions[5],
             allowedMimeTypes[4],
             externalAllowedFileMimeTypes[1],
             externalAllowedTextMimeTypes[1];
    
    jsiegle's avatar
    jsiegle committed
        static const unsigned long DndVersion;
    
    jsiegle's avatar
    jsiegle committed
        static Atom getIfExists (const char* name)    { return XInternAtom (display, name, True); }
        static Atom getCreating (const char* name)    { return XInternAtom (display, name, False); }
    
    jsiegle's avatar
    jsiegle committed
        static String getName (const Atom atom)
        {
            if (atom == None)
                return "None";
    
    jsiegle's avatar
    jsiegle committed
            return String (XGetAtomName (display, atom));
        }
    
    jsiegle's avatar
    jsiegle committed
        static bool isMimeTypeFile (const Atom atom)  { return getName (atom).equalsIgnoreCase ("text/uri-list"); }
    };
    
    jsiegle's avatar
    jsiegle committed
    const unsigned long Atoms::DndVersion = 3;
    
    jsiegle's avatar
    jsiegle committed
    //==============================================================================
    struct GetXProperty
    {
        GetXProperty (Window window, Atom atom, long offset, long length, bool shouldDelete, Atom requestedType)
            : data (nullptr)
        {
            success = (XGetWindowProperty (display, window, atom, offset, length,
                                           (Bool) shouldDelete, requestedType, &actualType,
                                           &actualFormat, &numItems, &bytesLeft, &data) == Success)
                        && data != nullptr;
        }
    
    jsiegle's avatar
    jsiegle committed
        ~GetXProperty()
        {
            if (data != nullptr)
                XFree (data);
    
    jsiegle's avatar
    jsiegle committed
    
        bool success;
        unsigned char* data;
        unsigned long numItems, bytesLeft;
        Atom actualType;
        int actualFormat;
    };
    
    
    //==============================================================================
    namespace Keys
    {
        enum MouseButtons
        {
            NoButton = 0,
            LeftButton = 1,
            MiddleButton = 2,
            RightButton = 3,
            WheelUp = 4,
            WheelDown = 5
        };
    
        static int AltMask = 0;
        static int NumLockMask = 0;
        static bool numLock = false;
        static bool capsLock = false;
        static char keyStates [32];
        static const int extendedKeyModifier = 0x10000000;
    }
    
    bool KeyPress::isKeyCurrentlyDown (const int keyCode)
    {
        int keysym;
    
        if (keyCode & Keys::extendedKeyModifier)
        {
            keysym = 0xff00 | (keyCode & 0xff);
        }
        else
        {
            keysym = keyCode;
    
            if (keysym == (XK_Tab & 0xff)
                || keysym == (XK_Return & 0xff)
                || keysym == (XK_Escape & 0xff)
                || keysym == (XK_BackSpace & 0xff))
            {
                keysym |= 0xff00;
            }
        }
    
        ScopedXLock xlock;
    
        const int keycode = XKeysymToKeycode (display, keysym);
    
        const int keybyte = keycode >> 3;
        const int keybit = (1 << (keycode & 7));
        return (Keys::keyStates [keybyte] & keybit) != 0;
    }
    
    //==============================================================================
    #if JUCE_USE_XSHM
    namespace XSHMHelpers
    {
        static int trappedErrorCode = 0;
        extern "C" int errorTrapHandler (Display*, XErrorEvent* err)
        {
            trappedErrorCode = err->error_code;
            return 0;
        }
    
    
    jsiegle's avatar
    jsiegle committed
        static bool isShmAvailable() noexcept
    
        {
            static bool isChecked = false;
            static bool isAvailable = false;
    
            if (! isChecked)
            {
                isChecked = true;
                int major, minor;
                Bool pixmaps;
    
                ScopedXLock xlock;
    
                if (XShmQueryVersion (display, &major, &minor, &pixmaps))
                {
                    trappedErrorCode = 0;
                    XErrorHandler oldHandler = XSetErrorHandler (errorTrapHandler);
    
                    XShmSegmentInfo segmentInfo;
                    zerostruct (segmentInfo);
    
    jsiegle's avatar
    jsiegle committed
    
    
                    XImage* xImage = XShmCreateImage (display, DefaultVisual (display, DefaultScreen (display)),
                                                      24, ZPixmap, 0, &segmentInfo, 50, 50);
    
                    if ((segmentInfo.shmid = shmget (IPC_PRIVATE,
                                                     xImage->bytes_per_line * xImage->height,
                                                     IPC_CREAT | 0777)) >= 0)
                    {
                        segmentInfo.shmaddr = (char*) shmat (segmentInfo.shmid, 0, 0);
    
                        if (segmentInfo.shmaddr != (void*) -1)
                        {
                            segmentInfo.readOnly = False;
                            xImage->data = segmentInfo.shmaddr;
                            XSync (display, False);
    
                            if (XShmAttach (display, &segmentInfo) != 0)
                            {
                                XSync (display, False);
                                XShmDetach (display, &segmentInfo);
    
                                isAvailable = true;
                            }
                        }
    
                        XFlush (display);
                        XDestroyImage (xImage);
    
                        shmdt (segmentInfo.shmaddr);
                    }
    
                    shmctl (segmentInfo.shmid, IPC_RMID, 0);
    
                    XSetErrorHandler (oldHandler);
                    if (trappedErrorCode != 0)
                        isAvailable = false;
                }
            }
    
            return isAvailable;
        }
    }
    #endif
    
    //==============================================================================
    #if JUCE_USE_XRENDER
    namespace XRender
    {
        typedef Status (*tXRenderQueryVersion) (Display*, int*, int*);
    
    jsiegle's avatar
    jsiegle committed
        typedef XRenderPictFormat* (*tXRenderFindStandardFormat) (Display*, int);
    
        typedef XRenderPictFormat* (*tXRenderFindFormat) (Display*, unsigned long, XRenderPictFormat*, int);
        typedef XRenderPictFormat* (*tXRenderFindVisualFormat) (Display*, Visual*);
    
    
    jsiegle's avatar
    jsiegle committed
        static tXRenderQueryVersion xRenderQueryVersion = nullptr;
        static tXRenderFindStandardFormat xRenderFindStandardFormat = nullptr;
        static tXRenderFindFormat xRenderFindFormat = nullptr;
        static tXRenderFindVisualFormat xRenderFindVisualFormat = nullptr;
    
    
        static bool isAvailable()
        {
            static bool hasLoaded = false;
    
            if (! hasLoaded)
            {
                ScopedXLock xlock;
                hasLoaded = true;
    
    
    jsiegle's avatar
    jsiegle committed
                if (void* h = dlopen ("libXrender.so", RTLD_GLOBAL | RTLD_NOW))
    
                {
                    xRenderQueryVersion         = (tXRenderQueryVersion)        dlsym (h, "XRenderQueryVersion");
    
    jsiegle's avatar
    jsiegle committed
                    xRenderFindStandardFormat   = (tXRenderFindStandardFormat)  dlsym (h, "XRenderFindStandardFormat");
    
                    xRenderFindFormat           = (tXRenderFindFormat)          dlsym (h, "XRenderFindFormat");
                    xRenderFindVisualFormat     = (tXRenderFindVisualFormat)    dlsym (h, "XRenderFindVisualFormat");
                }
    
    
    jsiegle's avatar
    jsiegle committed
                if (xRenderQueryVersion != nullptr
                     && xRenderFindStandardFormat != nullptr
                     && xRenderFindFormat != nullptr
                     && xRenderFindVisualFormat != nullptr)
    
                {
                    int major, minor;
                    if (xRenderQueryVersion (display, &major, &minor))
                        return true;
                }
    
    
    jsiegle's avatar
    jsiegle committed
                xRenderQueryVersion = nullptr;
    
    jsiegle's avatar
    jsiegle committed
            return xRenderQueryVersion != nullptr;
    
        }
    
        static XRenderPictFormat* findPictureFormat()
        {
            ScopedXLock xlock;
    
    jsiegle's avatar
    jsiegle committed
            XRenderPictFormat* pictFormat = nullptr;
    
    
            if (isAvailable())
            {
                pictFormat = xRenderFindStandardFormat (display, PictStandardARGB32);
    
    
    jsiegle's avatar
    jsiegle committed
                if (pictFormat == nullptr)
    
                {
                    XRenderPictFormat desiredFormat;
                    desiredFormat.type = PictTypeDirect;
                    desiredFormat.depth = 32;
    
                    desiredFormat.direct.alphaMask = 0xff;
    
    jsiegle's avatar
    jsiegle committed
                    desiredFormat.direct.redMask   = 0xff;
    
                    desiredFormat.direct.greenMask = 0xff;
    
    jsiegle's avatar
    jsiegle committed
                    desiredFormat.direct.blueMask  = 0xff;
    
    
                    desiredFormat.direct.alpha = 24;
    
    jsiegle's avatar
    jsiegle committed
                    desiredFormat.direct.red   = 16;
    
                    desiredFormat.direct.green = 8;
    
    jsiegle's avatar
    jsiegle committed
                    desiredFormat.direct.blue  = 0;
    
    
                    pictFormat = xRenderFindFormat (display,
                                                    PictFormatType | PictFormatDepth
                                                     | PictFormatRedMask | PictFormatRed
                                                     | PictFormatGreenMask | PictFormatGreen
                                                     | PictFormatBlueMask | PictFormatBlue
                                                     | PictFormatAlphaMask | PictFormatAlpha,
                                                    &desiredFormat,
                                                    0);
                }
            }
    
            return pictFormat;
        }
    }
    #endif
    
    //==============================================================================
    namespace Visuals
    {
    
    jsiegle's avatar
    jsiegle committed
        static Visual* findVisualWithDepth (const int desiredDepth) noexcept
    
        {
            ScopedXLock xlock;
    
    
    jsiegle's avatar
    jsiegle committed
            Visual* visual = nullptr;
    
            int numVisuals = 0;
            long desiredMask = VisualNoMask;
            XVisualInfo desiredVisual;
    
            desiredVisual.screen = DefaultScreen (display);
            desiredVisual.depth = desiredDepth;
    
            desiredMask = VisualScreenMask | VisualDepthMask;
    
            if (desiredDepth == 32)
            {
    
    jsiegle's avatar
    jsiegle committed
                desiredVisual.c_class    = TrueColor;
                desiredVisual.red_mask   = 0x00FF0000;
    
                desiredVisual.green_mask = 0x0000FF00;
    
    jsiegle's avatar
    jsiegle committed
                desiredVisual.blue_mask  = 0x000000FF;
    
                desiredVisual.bits_per_rgb = 8;
    
                desiredMask |= VisualClassMask;
                desiredMask |= VisualRedMaskMask;
                desiredMask |= VisualGreenMaskMask;
                desiredMask |= VisualBlueMaskMask;
                desiredMask |= VisualBitsPerRGBMask;
            }
    
            XVisualInfo* xvinfos = XGetVisualInfo (display,
                                                   desiredMask,
                                                   &desiredVisual,
                                                   &numVisuals);
    
    
    jsiegle's avatar
    jsiegle committed
            if (xvinfos != nullptr)
    
            {
                for (int i = 0; i < numVisuals; i++)
                {
                    if (xvinfos[i].depth == desiredDepth)
                    {
                        visual = xvinfos[i].visual;
                        break;
                    }
                }
    
                XFree (xvinfos);
            }
    
            return visual;
        }
    
    
    jsiegle's avatar
    jsiegle committed
        static Visual* findVisualFormat (const int desiredDepth, int& matchedDepth) noexcept
    
    jsiegle's avatar
    jsiegle committed
            Visual* visual = nullptr;
    
    
            if (desiredDepth == 32)
            {
    
    jsiegle's avatar
    jsiegle committed
               #if JUCE_USE_XSHM
    
                if (XSHMHelpers::isShmAvailable())
                {
    
    jsiegle's avatar
    jsiegle committed
                   #if JUCE_USE_XRENDER
    
                    if (XRender::isAvailable())
                    {
                        XRenderPictFormat* pictFormat = XRender::findPictureFormat();
    
                        if (pictFormat != 0)
                        {
                            int numVisuals = 0;
                            XVisualInfo desiredVisual;
                            desiredVisual.screen = DefaultScreen (display);
                            desiredVisual.depth = 32;
                            desiredVisual.bits_per_rgb = 8;
    
                            XVisualInfo* xvinfos = XGetVisualInfo (display,
                                                                   VisualScreenMask | VisualDepthMask | VisualBitsPerRGBMask,
                                                                   &desiredVisual, &numVisuals);
    
    jsiegle's avatar
    jsiegle committed
                            if (xvinfos != nullptr)
    
                            {
                                for (int i = 0; i < numVisuals; ++i)
                                {
                                    XRenderPictFormat* pictVisualFormat = XRender::xRenderFindVisualFormat (display, xvinfos[i].visual);
    
    
    jsiegle's avatar
    jsiegle committed
                                    if (pictVisualFormat != nullptr
    
                                         && pictVisualFormat->type == PictTypeDirect
                                         && pictVisualFormat->direct.alphaMask)
                                    {
                                        visual = xvinfos[i].visual;
                                        matchedDepth = 32;
                                        break;
                                    }
                                }
    
                                XFree (xvinfos);
                            }
                        }
                    }
    
    jsiegle's avatar
    jsiegle committed
                   #endif
                    if (visual == nullptr)
    
                    {
                        visual = findVisualWithDepth (32);
    
    jsiegle's avatar
    jsiegle committed
                        if (visual != nullptr)
    
                            matchedDepth = 32;
                    }
                }
    
    jsiegle's avatar
    jsiegle committed
               #endif
    
    jsiegle's avatar
    jsiegle committed
            if (visual == nullptr && desiredDepth >= 24)
    
            {
                visual = findVisualWithDepth (24);
    
    jsiegle's avatar
    jsiegle committed
                if (visual != nullptr)
    
                    matchedDepth = 24;
            }
    
    
    jsiegle's avatar
    jsiegle committed
            if (visual == nullptr && desiredDepth >= 16)
    
            {
                visual = findVisualWithDepth (16);
    
    jsiegle's avatar
    jsiegle committed
                if (visual != nullptr)
    
                    matchedDepth = 16;
            }
    
            return visual;
        }
    }
    
    //==============================================================================
    
    jsiegle's avatar
    jsiegle committed
    class XBitmapImage  : public ImagePixelData
    
    jsiegle's avatar
    jsiegle committed
        XBitmapImage (const Image::PixelFormat format, const int w, const int h,
    
                      const bool clearImage, const int imageDepth_, Visual* visual)
    
    jsiegle's avatar
    jsiegle committed
            : ImagePixelData (format, w, h),
    
              imageDepth (imageDepth_),
              gc (None)
        {
    
    jsiegle's avatar
    jsiegle committed
            jassert (format == Image::RGB || format == Image::ARGB);
    
    jsiegle's avatar
    jsiegle committed
            pixelStride = (format == Image::RGB) ? 3 : 4;
    
            lineStride = ((w * pixelStride + 3) & ~3);
    
            ScopedXLock xlock;
    
    
    jsiegle's avatar
    jsiegle committed
           #if JUCE_USE_XSHM
    
            usingXShm = false;
    
            if ((imageDepth > 16) && XSHMHelpers::isShmAvailable())
            {
                zerostruct (segmentInfo);
    
                segmentInfo.shmid = -1;
                segmentInfo.shmaddr = (char *) -1;
                segmentInfo.readOnly = False;
    
                xImage = XShmCreateImage (display, visual, imageDepth, ZPixmap, 0, &segmentInfo, w, h);
    
    
    jsiegle's avatar
    jsiegle committed
                if (xImage != nullptr)
    
                {
                    if ((segmentInfo.shmid = shmget (IPC_PRIVATE,
                                                     xImage->bytes_per_line * xImage->height,
                                                     IPC_CREAT | 0777)) >= 0)
                    {
                        if (segmentInfo.shmid != -1)
                        {
                            segmentInfo.shmaddr = (char*) shmat (segmentInfo.shmid, 0, 0);
    
                            if (segmentInfo.shmaddr != (void*) -1)
                            {
                                segmentInfo.readOnly = False;
    
                                xImage->data = segmentInfo.shmaddr;
                                imageData = (uint8*) segmentInfo.shmaddr;
    
                                if (XShmAttach (display, &segmentInfo) != 0)
                                    usingXShm = true;
                                else
                                    jassertfalse;
                            }
                            else
                            {
                                shmctl (segmentInfo.shmid, IPC_RMID, 0);
                            }
                        }
                    }
                }
            }
    
            if (! usingXShm)
    
    jsiegle's avatar
    jsiegle committed
           #endif
    
    jsiegle's avatar
    jsiegle committed
                imageDataAllocated.allocate (lineStride * h, format == Image::ARGB && clearImage);
    
                imageData = imageDataAllocated;
    
    
    jsiegle's avatar
    jsiegle committed
                xImage = (XImage*) ::calloc (1, sizeof (XImage));
    
    
                xImage->width = w;
                xImage->height = h;
                xImage->xoffset = 0;
                xImage->format = ZPixmap;
                xImage->data = (char*) imageData;
                xImage->byte_order = ImageByteOrder (display);
                xImage->bitmap_unit = BitmapUnit (display);
                xImage->bitmap_bit_order = BitmapBitOrder (display);
                xImage->bitmap_pad = 32;
                xImage->depth = pixelStride * 8;
                xImage->bytes_per_line = lineStride;
                xImage->bits_per_pixel = pixelStride * 8;
                xImage->red_mask   = 0x00FF0000;
                xImage->green_mask = 0x0000FF00;
                xImage->blue_mask  = 0x000000FF;
    
                if (imageDepth == 16)
                {
                    const int pixelStride = 2;
                    const int lineStride = ((w * pixelStride + 3) & ~3);
    
                    imageData16Bit.malloc (lineStride * h);
                    xImage->data = imageData16Bit;
                    xImage->bitmap_pad = 16;
                    xImage->depth = pixelStride * 8;
                    xImage->bytes_per_line = lineStride;
                    xImage->bits_per_pixel = pixelStride * 8;
                    xImage->red_mask   = visual->red_mask;
                    xImage->green_mask = visual->green_mask;
                    xImage->blue_mask  = visual->blue_mask;
                }
    
                if (! XInitImage (xImage))
                    jassertfalse;
            }
        }
    
        ~XBitmapImage()
        {
            ScopedXLock xlock;
    
            if (gc != None)
                XFreeGC (display, gc);
    
    
    jsiegle's avatar
    jsiegle committed
           #if JUCE_USE_XSHM
    
            if (usingXShm)
            {
                XShmDetach (display, &segmentInfo);
    
                XFlush (display);
                XDestroyImage (xImage);
    
                shmdt (segmentInfo.shmaddr);
                shmctl (segmentInfo.shmid, IPC_RMID, 0);
            }
            else
    
    jsiegle's avatar
    jsiegle committed
           #endif
    
    jsiegle's avatar
    jsiegle committed
                xImage->data = nullptr;
    
                XDestroyImage (xImage);
            }
        }
    
        LowLevelGraphicsContext* createLowLevelContext()
        {
            return new LowLevelGraphicsSoftwareRenderer (Image (this));
        }
    
    
    jsiegle's avatar
    jsiegle committed
        void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode)
        {
            bitmap.data = imageData + x * pixelStride + y * lineStride;
            bitmap.pixelFormat = pixelFormat;
            bitmap.lineStride = lineStride;
            bitmap.pixelStride = pixelStride;
        }
    
        ImagePixelData* clone()
    
        {
            jassertfalse;
    
    jsiegle's avatar
    jsiegle committed
            return nullptr;
    
    jsiegle's avatar
    jsiegle committed
        ImageType* createType() const     { return new NativeImageType(); }
    
    
        void blitToWindow (Window window, int dx, int dy, int dw, int dh, int sx, int sy)
        {
            ScopedXLock xlock;
    
            if (gc == None)
            {
                XGCValues gcvalues;
                gcvalues.foreground = None;
                gcvalues.background = None;
                gcvalues.function = GXcopy;
                gcvalues.plane_mask = AllPlanes;
                gcvalues.clip_mask = None;
                gcvalues.graphics_exposures = False;
    
                gc = XCreateGC (display, window,
                                GCBackground | GCForeground | GCFunction | GCPlaneMask | GCClipMask | GCGraphicsExposures,
                                &gcvalues);
            }
    
            if (imageDepth == 16)
            {
    
    jsiegle's avatar
    jsiegle committed
                const uint32 rMask   = xImage->red_mask;
                const uint32 gMask   = xImage->green_mask;
                const uint32 bMask   = xImage->blue_mask;
                const uint32 rShiftL = jmax (0,  getShiftNeeded (rMask));
    
                const uint32 rShiftR = jmax (0, -getShiftNeeded (rMask));
    
    jsiegle's avatar
    jsiegle committed
                const uint32 gShiftL = jmax (0,  getShiftNeeded (gMask));
    
                const uint32 gShiftR = jmax (0, -getShiftNeeded (gMask));
    
    jsiegle's avatar
    jsiegle committed
                const uint32 bShiftL = jmax (0,  getShiftNeeded (bMask));
    
                const uint32 bShiftR = jmax (0, -getShiftNeeded (bMask));
    
    
    jsiegle's avatar
    jsiegle committed
                const Image::BitmapData srcData (Image (this), Image::BitmapData::readOnly);
    
    
                for (int y = sy; y < sy + dh; ++y)
                {
                    const uint8* p = srcData.getPixelPointer (sx, y);
    
                    for (int x = sx; x < sx + dw; ++x)
                    {
                        const PixelRGB* const pixel = (const PixelRGB*) p;
                        p += srcData.pixelStride;
    
                        XPutPixel (xImage, x, y,
                                   (((((uint32) pixel->getRed()) << rShiftL) >> rShiftR) & rMask)
                                     | (((((uint32) pixel->getGreen()) << gShiftL) >> gShiftR) & gMask)
                                     | (((((uint32) pixel->getBlue()) << bShiftL) >> bShiftR) & bMask));
                    }
                }
            }
    
            // blit results to screen.
    
    jsiegle's avatar
    jsiegle committed
           #if JUCE_USE_XSHM
    
            if (usingXShm)
                XShmPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh, True);
            else
    
    jsiegle's avatar
    jsiegle committed
           #endif
    
                XPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh);
        }
    
    private:
    
    jsiegle's avatar
    jsiegle committed
        //==============================================================================
    
        XImage* xImage;
        const int imageDepth;
        HeapBlock <uint8> imageDataAllocated;
        HeapBlock <char> imageData16Bit;
    
    jsiegle's avatar
    jsiegle committed
        int pixelStride, lineStride;
        uint8* imageData;
    
    jsiegle's avatar
    jsiegle committed
       #if JUCE_USE_XSHM
    
        XShmSegmentInfo segmentInfo;
        bool usingXShm;
    
    jsiegle's avatar
    jsiegle committed
       #endif
    
    jsiegle's avatar
    jsiegle committed
        static int getShiftNeeded (const uint32 mask) noexcept
    
        {
            for (int i = 32; --i >= 0;)
                if (((mask >> i) & 1) != 0)
                    return i - 7;
    
            jassertfalse;
            return 0;
        }
    
    
    jsiegle's avatar
    jsiegle committed
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XBitmapImage)
    
    jsiegle's avatar
    jsiegle committed
    //==============================================================================
    
    namespace PixmapHelpers
    {
        Pixmap createColourPixmapFromImage (Display* display, const Image& image)
        {
            ScopedXLock xlock;
    
            const int width = image.getWidth();
            const int height = image.getHeight();
            HeapBlock <uint32> colour (width * height);
            int index = 0;
    
            for (int y = 0; y < height; ++y)
                for (int x = 0; x < width; ++x)
                    colour[index++] = image.getPixelAt (x, y).getARGB();
    
            XImage* ximage = XCreateImage (display, CopyFromParent, 24, ZPixmap,
                                           0, reinterpret_cast<char*> (colour.getData()),
                                           width, height, 32, 0);
    
            Pixmap pixmap = XCreatePixmap (display, DefaultRootWindow (display),
                                           width, height, 24);
    
            GC gc = XCreateGC (display, pixmap, 0, 0);
            XPutImage (display, pixmap, gc, ximage, 0, 0, 0, 0, width, height);
            XFreeGC (display, gc);
    
            return pixmap;
        }
    
        Pixmap createMaskPixmapFromImage (Display* display, const Image& image)
        {
            ScopedXLock xlock;
    
            const int width = image.getWidth();
            const int height = image.getHeight();
            const int stride = (width + 7) >> 3;
            HeapBlock <char> mask;
            mask.calloc (stride * height);
            const bool msbfirst = (BitmapBitOrder (display) == MSBFirst);
    
            for (int y = 0; y < height; ++y)
            {
                for (int x = 0; x < width; ++x)
                {
                    const char bit = (char) (1 << (msbfirst ? (7 - (x & 7)) : (x & 7)));
                    const int offset = y * stride + (x >> 3);
    
                    if (image.getPixelAt (x, y).getAlpha() >= 128)
                        mask[offset] |= bit;
                }
            }
    
            return XCreatePixmapFromBitmapData (display, DefaultRootWindow (display),
                                                mask.getData(), width, height, 1, 0, 1);
        }
    }
    
    
    jsiegle's avatar
    jsiegle committed
    static void* createDraggingHandCursor()
    {
        static unsigned char dragHandData[] = { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,
          0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39,
          132,117,151,116,132,146,248,60,209,138,98,22,203,114,34,236,37,52,77,217, 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 };
        const int dragHandDataSize = 99;
    
        return CustomMouseCursorInfo (ImageFileFormat::loadFrom (dragHandData, dragHandDataSize), 8, 7).create();
    }
    
    
    //==============================================================================
    class LinuxComponentPeer  : public ComponentPeer
    {
    public:
    
    jsiegle's avatar
    jsiegle committed
        LinuxComponentPeer (Component& comp, const int windowStyleFlags, Window parentToAddTo)
            : ComponentPeer (comp, windowStyleFlags),
              windowH (0), parentWindow (0),
              fullScreen (false), mapped (false),
              visual (nullptr), depth (0)
    
        {
            // it's dangerous to create a window on a thread other than the message thread..
            jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
    
    
    jsiegle's avatar
    jsiegle committed
            dispatchWindowMessage = windowMessageReceive;
    
            repainter = new LinuxRepaintManager (this);
    
    
    jsiegle's avatar
    jsiegle committed
            createWindow (parentToAddTo);
    
    jsiegle's avatar
    jsiegle committed
            setTitle (component.getName());
    
        }
    
        ~LinuxComponentPeer()
        {
            // it's dangerous to delete a window on a thread other than the message thread..
            jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
    
            deleteIconPixmaps();
            destroyWindow();
            windowH = 0;
        }
    
    
    jsiegle's avatar
    jsiegle committed
        // (this callback is hooked up in the messaging code)
        static void windowMessageReceive (XEvent& event)
        {
            if (event.xany.window != None)
            {
                if (LinuxComponentPeer* const peer = getPeerFor (event.xany.window))
                    peer->handleWindowMessage (event);
            }
            else if (event.xany.type == KeymapNotify)
            {
                const XKeymapEvent& keymapEvent = (const XKeymapEvent&) event.xkeymap;
                memcpy (Keys::keyStates, keymapEvent.key_vector, 32);
            }
        }
    
    
        //==============================================================================
        void* getNativeHandle() const
        {
            return (void*) windowH;
        }
    
    
    jsiegle's avatar
    jsiegle committed
        static LinuxComponentPeer* getPeerFor (Window windowHandle) noexcept
    
    jsiegle's avatar
    jsiegle committed
            XPointer peer = nullptr;
    
    
            ScopedXLock xlock;
            if (! XFindContext (display, (XID) windowHandle, windowHandleXContext, &peer))
    
    jsiegle's avatar
    jsiegle committed
                if (peer != nullptr && ! ComponentPeer::isValidPeer (reinterpret_cast <LinuxComponentPeer*> (peer)))
                    peer = nullptr;
    
    jsiegle's avatar
    jsiegle committed
            return reinterpret_cast <LinuxComponentPeer*> (peer);
    
        }
    
        void setVisible (bool shouldBeVisible)
        {
            ScopedXLock xlock;
            if (shouldBeVisible)
                XMapWindow (display, windowH);
            else
                XUnmapWindow (display, windowH);
        }
    
        void setTitle (const String& title)
        {
            XTextProperty nameProperty;
    
    jsiegle's avatar
    jsiegle committed
            char* strings[] = { const_cast <char*> (title.toUTF8().getAddress()) };
    
            ScopedXLock xlock;
    
            if (XStringListToTextProperty (strings, 1, &nameProperty))
            {
                XSetWMName (display, windowH, &nameProperty);
                XSetWMIconName (display, windowH, &nameProperty);
    
                XFree (nameProperty.value);
            }
        }
    
        void setBounds (int x, int y, int w, int h, bool isNowFullScreen)
        {
    
    jsiegle's avatar
    jsiegle committed
            if (fullScreen && ! isNowFullScreen)
            {
                // When transitioning back from fullscreen, we might need to remove
                // the FULLSCREEN window property
                Atom fs = Atoms::getIfExists ("_NET_WM_STATE_FULLSCREEN");
    
    jsiegle's avatar
    jsiegle committed
                if (fs != None)
                {
                    Window root = RootWindow (display, DefaultScreen (display));
    
                    XClientMessageEvent clientMsg;
                    clientMsg.display = display;
                    clientMsg.window = windowH;
                    clientMsg.type = ClientMessage;
                    clientMsg.format = 32;
                    clientMsg.message_type = Atoms::get().WindowState;
                    clientMsg.data.l[0] = 0;  // Remove
                    clientMsg.data.l[1] = fs;
                    clientMsg.data.l[2] = 0;
                    clientMsg.data.l[3] = 1;  // Normal Source
    
                    ScopedXLock xlock;
                    XSendEvent (display, root, false,
                                SubstructureRedirectMask | SubstructureNotifyMask,
                                (XEvent*) &clientMsg);
                }
            }
    
            fullScreen = isNowFullScreen;
    
            if (windowH != 0)
            {
    
    jsiegle's avatar
    jsiegle committed
                bounds.setBounds (x, y, jmax (1, w), jmax (1, h));
    
    jsiegle's avatar
    jsiegle committed
                WeakReference<Component> deletionChecker (&component);
    
                ScopedXLock xlock;
    
    
    jsiegle's avatar
    jsiegle committed
                XSizeHints* const hints = XAllocSizeHints();
                hints->flags  = USSize | USPosition;
                hints->x      = bounds.getX();
                hints->y      = bounds.getY();
                hints->width  = bounds.getWidth();
                hints->height = bounds.getHeight();
    
    
                if ((getStyleFlags() & (windowHasTitleBar | windowIsResizable)) == windowHasTitleBar)
                {
                    hints->min_width  = hints->max_width  = hints->width;
                    hints->min_height = hints->max_height = hints->height;
                    hints->flags |= PMinSize | PMaxSize;
                }
    
                XSetWMNormalHints (display, windowH, hints);
                XFree (hints);
    
                XMoveResizeWindow (display, windowH,
    
    jsiegle's avatar
    jsiegle committed
                                   bounds.getX() - windowBorder.getLeft(),
                                   bounds.getY() - windowBorder.getTop(),
                                   bounds.getWidth(),
                                   bounds.getHeight());
    
    jsiegle's avatar
    jsiegle committed
                if (deletionChecker != nullptr)
    
                {
                    updateBorderSize();
                    handleMovedOrResized();
                }
            }
        }
    
    
    jsiegle's avatar
    jsiegle committed
        void setPosition (int x, int y)           { setBounds (x, y, bounds.getWidth(), bounds.getHeight(), false); }
        void setSize (int w, int h)               { setBounds (bounds.getX(), bounds.getY(), w, h, false); }
        Rectangle<int> getBounds() const          { return bounds; }
        Point<int> getScreenPosition() const      { return bounds.getPosition(); }
    
    jsiegle's avatar
    jsiegle committed
        Point<int> localToGlobal (const Point<int>& relativePosition)
    
        {
            return relativePosition + getScreenPosition();
        }
    
    
    jsiegle's avatar
    jsiegle committed
        Point<int> globalToLocal (const Point<int>& screenPosition)
    
        {
            return screenPosition - getScreenPosition();
        }
    
    
    jsiegle's avatar
    jsiegle committed
        void setAlpha (float /* newAlpha */)
    
        {
            //xxx todo!
        }
    
        void setMinimised (bool shouldBeMinimised)
        {
            if (shouldBeMinimised)
            {
                Window root = RootWindow (display, DefaultScreen (display));
    
                XClientMessageEvent clientMsg;
                clientMsg.display = display;
                clientMsg.window = windowH;
                clientMsg.type = ClientMessage;
                clientMsg.format = 32;
    
    jsiegle's avatar
    jsiegle committed
                clientMsg.message_type = Atoms::get().ChangeState;
    
                clientMsg.data.l[0] = IconicState;
    
                ScopedXLock xlock;
                XSendEvent (display, root, false, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*) &clientMsg);
            }
            else
            {
                setVisible (true);
            }
        }
    
        bool isMinimised() const
        {
            ScopedXLock xlock;
    
    jsiegle's avatar
    jsiegle committed
            const Atoms& atoms = Atoms::get();
            GetXProperty prop (windowH, atoms.State, 0, 64, false, atoms.State);