Skip to content
Snippets Groups Projects
juce_linux_Windowing.cpp 152 KiB
Newer Older
  • Learn to ignore specific revisions
  • Josh Siegle's avatar
    Josh Siegle committed
    /*
      ==============================================================================
    
       This file is part of the JUCE library.
    
    Septen's avatar
    Septen committed
       Copyright (c) 2015 - ROLI Ltd.
    
    Josh Siegle's avatar
    Josh Siegle committed
    
       Permission is granted to use this software under the terms of either:
       a) the GPL v2 (or any later version)
       b) the Affero GPL v3
    
       Details of these licenses can be found 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.juce.com for more information.
    
      ==============================================================================
    */
    
    
    Septen's avatar
    Septen committed
    extern ::Display* display;
    
    Josh Siegle's avatar
    Josh Siegle committed
    extern XContext windowHandleXContext;
    typedef void (*WindowMessageReceiveCallback) (XEvent&);
    extern WindowMessageReceiveCallback dispatchWindowMessage;
    
    
    //==============================================================================
    struct Atoms
    {
        Atoms()
        {
    
    Septen's avatar
    Septen committed
            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");
    
    Josh Siegle's avatar
    Josh Siegle committed
    
            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");
    
            allowedActions[0]               = getCreating ("XdndActionMove");
            allowedActions[1]               = XdndActionCopy;
            allowedActions[2]               = getCreating ("XdndActionLink");
            allowedActions[3]               = getCreating ("XdndActionAsk");
            allowedActions[4]               = XdndActionPrivate;
        }
    
        enum ProtocolItems
        {
            TAKE_FOCUS = 0,
            DELETE_WINDOW = 1,
            PING = 2
        };
    
    
    Septen's avatar
    Septen committed
        Atom protocols, protocolList[3], changeState, state, userTime,
             activeWin, pid, windowType, windowState,
    
    Josh Siegle's avatar
    Josh Siegle committed
             XdndAware, XdndEnter, XdndLeave, XdndPosition, XdndStatus,
             XdndDrop, XdndFinished, XdndSelection, XdndTypeList, XdndActionList,
             XdndActionDescription, XdndActionCopy, XdndActionPrivate,
             allowedActions[5],
    
    Septen's avatar
    Septen committed
             allowedMimeTypes[4];
    
    Josh Siegle's avatar
    Josh Siegle committed
    
        static const unsigned long DndVersion;
    
        static Atom getIfExists (const char* name)    { return XInternAtom (display, name, True); }
        static Atom getCreating (const char* name)    { return XInternAtom (display, name, False); }
    
        static String getName (const Atom atom)
        {
            if (atom == None)
                return "None";
    
            return String (XGetAtomName (display, atom));
        }
    
        static bool isMimeTypeFile (const Atom atom)  { return getName (atom).equalsIgnoreCase ("text/uri-list"); }
    };
    
    const unsigned long Atoms::DndVersion = 3;
    
    //==============================================================================
    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;
        }
    
        ~GetXProperty()
        {
            if (data != nullptr)
                XFree (data);
        }
    
        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)
    {
    
    Septen's avatar
    Septen committed
        if (display == nullptr)
            return false;
    
    
    Josh Siegle's avatar
    Josh Siegle committed
        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;
    
    
    Septen's avatar
    Septen committed
        const int keycode = XKeysymToKeycode (display, (KeySym) keysym);
    
    Josh Siegle's avatar
    Josh Siegle committed
    
        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;
        }
    
        static bool isShmAvailable() noexcept
        {
            static bool isChecked = false;
            static bool isAvailable = false;
    
            if (! isChecked)
            {
                isChecked = true;
    
    
    Septen's avatar
    Septen committed
                if (display != nullptr)
    
    Josh Siegle's avatar
    Josh Siegle committed
                {
    
    Septen's avatar
    Septen committed
                    int major, minor;
                    Bool pixmaps;
    
    Septen's avatar
    Septen committed
                    ScopedXLock xlock;
    
    Septen's avatar
    Septen committed
                    if (XShmQueryVersion (display, &major, &minor, &pixmaps))
    
    Josh Siegle's avatar
    Josh Siegle committed
                    {
    
    Septen's avatar
    Septen committed
                        trappedErrorCode = 0;
                        XErrorHandler oldHandler = XSetErrorHandler (errorTrapHandler);
    
    Septen's avatar
    Septen committed
                        XShmSegmentInfo segmentInfo;
                        zerostruct (segmentInfo);
    
    Septen's avatar
    Septen committed
                        if (XImage* xImage = XShmCreateImage (display, DefaultVisual (display, DefaultScreen (display)),
                                                              24, ZPixmap, 0, &segmentInfo, 50, 50))
                        {
                            if ((segmentInfo.shmid = shmget (IPC_PRIVATE,
                                                             (size_t) (xImage->bytes_per_line * xImage->height),
                                                             IPC_CREAT | 0777)) >= 0)
    
    Josh Siegle's avatar
    Josh Siegle committed
                            {
    
    Septen's avatar
    Septen committed
                                segmentInfo.shmaddr = (char*) shmat (segmentInfo.shmid, 0, 0);
    
    Septen's avatar
    Septen committed
                                if (segmentInfo.shmaddr != (void*) -1)
                                {
                                    segmentInfo.readOnly = False;
                                    xImage->data = segmentInfo.shmaddr;
                                    XSync (display, False);
    
    Septen's avatar
    Septen committed
                                    if (XShmAttach (display, &segmentInfo) != 0)
                                    {
                                        XSync (display, False);
                                        XShmDetach (display, &segmentInfo);
    
    Septen's avatar
    Septen committed
                                        isAvailable = true;
                                    }
                                }
    
    Septen's avatar
    Septen committed
                                XFlush (display);
                                XDestroyImage (xImage);
    
                                shmdt (segmentInfo.shmaddr);
                            }
    
    Septen's avatar
    Septen committed
                            shmctl (segmentInfo.shmid, IPC_RMID, 0);
    
                            XSetErrorHandler (oldHandler);
                            if (trappedErrorCode != 0)
                                isAvailable = false;
                        }
                    }
    
    Josh Siegle's avatar
    Josh Siegle committed
                }
            }
    
            return isAvailable;
        }
    }
    #endif
    
    //==============================================================================
    #if JUCE_USE_XRENDER
    namespace XRender
    {
        typedef Status (*tXRenderQueryVersion) (Display*, int*, int*);
        typedef XRenderPictFormat* (*tXRenderFindStandardFormat) (Display*, int);
        typedef XRenderPictFormat* (*tXRenderFindFormat) (Display*, unsigned long, XRenderPictFormat*, int);
        typedef XRenderPictFormat* (*tXRenderFindVisualFormat) (Display*, Visual*);
    
        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)
            {
    
    Septen's avatar
    Septen committed
                if (display != nullptr)
    
    Josh Siegle's avatar
    Josh Siegle committed
                {
    
    Septen's avatar
    Septen committed
                    hasLoaded = true;
    
    Septen's avatar
    Septen committed
                    ScopedXLock xlock;
    
                    if (void* h = dlopen ("libXrender.so", RTLD_GLOBAL | RTLD_NOW))
                    {
                        xRenderQueryVersion         = (tXRenderQueryVersion)        dlsym (h, "XRenderQueryVersion");
                        xRenderFindStandardFormat   = (tXRenderFindStandardFormat)  dlsym (h, "XRenderFindStandardFormat");
                        xRenderFindFormat           = (tXRenderFindFormat)          dlsym (h, "XRenderFindFormat");
                        xRenderFindVisualFormat     = (tXRenderFindVisualFormat)    dlsym (h, "XRenderFindVisualFormat");
                    }
    
                    if (xRenderQueryVersion != nullptr
                         && xRenderFindStandardFormat != nullptr
                         && xRenderFindFormat != nullptr
                         && xRenderFindVisualFormat != nullptr)
                    {
                        int major, minor;
                        if (xRenderQueryVersion (display, &major, &minor))
                            return true;
                    }
    
    Josh Siegle's avatar
    Josh Siegle committed
                }
    
                xRenderQueryVersion = nullptr;
            }
    
            return xRenderQueryVersion != nullptr;
        }
    
    
    Septen's avatar
    Septen committed
        static bool hasCompositingWindowManager() noexcept
        {
            return display != nullptr
                    && XGetSelectionOwner (display, Atoms::getCreating ("_NET_WM_CM_S0")) != 0;
        }
    
    
    Josh Siegle's avatar
    Josh Siegle committed
        static XRenderPictFormat* findPictureFormat()
        {
            ScopedXLock xlock;
            XRenderPictFormat* pictFormat = nullptr;
    
            if (isAvailable())
            {
                pictFormat = xRenderFindStandardFormat (display, PictStandardARGB32);
    
                if (pictFormat == nullptr)
                {
                    XRenderPictFormat desiredFormat;
                    desiredFormat.type = PictTypeDirect;
                    desiredFormat.depth = 32;
    
                    desiredFormat.direct.alphaMask = 0xff;
                    desiredFormat.direct.redMask   = 0xff;
                    desiredFormat.direct.greenMask = 0xff;
                    desiredFormat.direct.blueMask  = 0xff;
    
                    desiredFormat.direct.alpha = 24;
                    desiredFormat.direct.red   = 16;
                    desiredFormat.direct.green = 8;
                    desiredFormat.direct.blue  = 0;
    
                    pictFormat = xRenderFindFormat (display,
                                                    PictFormatType | PictFormatDepth
                                                     | PictFormatRedMask | PictFormatRed
                                                     | PictFormatGreenMask | PictFormatGreen
                                                     | PictFormatBlueMask | PictFormatBlue
                                                     | PictFormatAlphaMask | PictFormatAlpha,
                                                    &desiredFormat,
                                                    0);
                }
            }
    
            return pictFormat;
        }
    }
    #endif
    
    //==============================================================================
    namespace Visuals
    {
        static Visual* findVisualWithDepth (const int desiredDepth) noexcept
        {
            ScopedXLock xlock;
    
            Visual* visual = nullptr;
            int numVisuals = 0;
            long desiredMask = VisualNoMask;
            XVisualInfo desiredVisual;
    
            desiredVisual.screen = DefaultScreen (display);
            desiredVisual.depth = desiredDepth;
    
            desiredMask = VisualScreenMask | VisualDepthMask;
    
            if (desiredDepth == 32)
            {
                desiredVisual.c_class    = TrueColor;
                desiredVisual.red_mask   = 0x00FF0000;
                desiredVisual.green_mask = 0x0000FF00;
                desiredVisual.blue_mask  = 0x000000FF;
                desiredVisual.bits_per_rgb = 8;
    
                desiredMask |= VisualClassMask;
                desiredMask |= VisualRedMaskMask;
                desiredMask |= VisualGreenMaskMask;
                desiredMask |= VisualBlueMaskMask;
                desiredMask |= VisualBitsPerRGBMask;
            }
    
    
    Septen's avatar
    Septen committed
            if (XVisualInfo* xvinfos = XGetVisualInfo (display,
                                                       desiredMask,
                                                       &desiredVisual,
                                                       &numVisuals))
    
    Josh Siegle's avatar
    Josh Siegle committed
            {
                for (int i = 0; i < numVisuals; i++)
                {
                    if (xvinfos[i].depth == desiredDepth)
                    {
                        visual = xvinfos[i].visual;
                        break;
                    }
                }
    
                XFree (xvinfos);
            }
    
            return visual;
        }
    
        static Visual* findVisualFormat (const int desiredDepth, int& matchedDepth) noexcept
        {
            Visual* visual = nullptr;
    
            if (desiredDepth == 32)
            {
               #if JUCE_USE_XSHM
                if (XSHMHelpers::isShmAvailable())
                {
                   #if JUCE_USE_XRENDER
                    if (XRender::isAvailable())
                    {
    
    Septen's avatar
    Septen committed
                        if (XRenderPictFormat* pictFormat = XRender::findPictureFormat())
    
    Josh Siegle's avatar
    Josh Siegle committed
                        {
                            int numVisuals = 0;
                            XVisualInfo desiredVisual;
                            desiredVisual.screen = DefaultScreen (display);
                            desiredVisual.depth = 32;
                            desiredVisual.bits_per_rgb = 8;
    
    
    Septen's avatar
    Septen committed
                            if (XVisualInfo* xvinfos = XGetVisualInfo (display,
                                                                       VisualScreenMask | VisualDepthMask | VisualBitsPerRGBMask,
                                                                       &desiredVisual, &numVisuals))
    
    Josh Siegle's avatar
    Josh Siegle committed
                            {
                                for (int i = 0; i < numVisuals; ++i)
                                {
                                    XRenderPictFormat* pictVisualFormat = XRender::xRenderFindVisualFormat (display, xvinfos[i].visual);
    
                                    if (pictVisualFormat != nullptr
                                         && pictVisualFormat->type == PictTypeDirect
                                         && pictVisualFormat->direct.alphaMask)
                                    {
                                        visual = xvinfos[i].visual;
                                        matchedDepth = 32;
                                        break;
                                    }
                                }
    
                                XFree (xvinfos);
                            }
                        }
                    }
                   #endif
                    if (visual == nullptr)
                    {
                        visual = findVisualWithDepth (32);
                        if (visual != nullptr)
                            matchedDepth = 32;
                    }
                }
               #endif
            }
    
            if (visual == nullptr && desiredDepth >= 24)
            {
                visual = findVisualWithDepth (24);
                if (visual != nullptr)
                    matchedDepth = 24;
            }
    
            if (visual == nullptr && desiredDepth >= 16)
            {
                visual = findVisualWithDepth (16);
                if (visual != nullptr)
                    matchedDepth = 16;
            }
    
            return visual;
        }
    }
    
    //==============================================================================
    class XBitmapImage  : public ImagePixelData
    {
    public:
        XBitmapImage (const Image::PixelFormat format, const int w, const int h,
    
    Septen's avatar
    Septen committed
                      const bool clearImage, const unsigned int imageDepth_, Visual* visual)
    
    Josh Siegle's avatar
    Josh Siegle committed
            : ImagePixelData (format, w, h),
              imageDepth (imageDepth_),
              gc (None)
        {
            jassert (format == Image::RGB || format == Image::ARGB);
    
            pixelStride = (format == Image::RGB) ? 3 : 4;
            lineStride = ((w * pixelStride + 3) & ~3);
    
            ScopedXLock xlock;
    
           #if JUCE_USE_XSHM
            usingXShm = false;
    
            if ((imageDepth > 16) && XSHMHelpers::isShmAvailable())
            {
                zerostruct (segmentInfo);
    
                segmentInfo.shmid = -1;
                segmentInfo.shmaddr = (char *) -1;
                segmentInfo.readOnly = False;
    
    
    Septen's avatar
    Septen committed
                xImage = XShmCreateImage (display, visual, imageDepth, ZPixmap, 0,
                                          &segmentInfo, (unsigned int) w, (unsigned int) h);
    
    Josh Siegle's avatar
    Josh Siegle committed
    
                if (xImage != nullptr)
                {
                    if ((segmentInfo.shmid = shmget (IPC_PRIVATE,
    
    Septen's avatar
    Septen committed
                                                     (size_t) (xImage->bytes_per_line * xImage->height),
    
    Josh Siegle's avatar
    Josh Siegle committed
                                                     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);
                            }
                        }
                    }
                }
            }
    
    
    Septen's avatar
    Septen committed
            if (! isUsingXShm())
    
    Josh Siegle's avatar
    Josh Siegle committed
           #endif
            {
    
    Septen's avatar
    Septen committed
                imageDataAllocated.allocate ((size_t) (lineStride * h), format == Image::ARGB && clearImage);
    
    Josh Siegle's avatar
    Josh Siegle committed
                imageData = imageDataAllocated;
    
                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)
                {
    
    Septen's avatar
    Septen committed
                    const int pixStride = 2;
                    const int stride = ((w * pixStride + 3) & ~3);
    
    Septen's avatar
    Septen committed
                    imageData16Bit.malloc ((size_t) (stride * h));
    
    Josh Siegle's avatar
    Josh Siegle committed
                    xImage->data = imageData16Bit;
                    xImage->bitmap_pad = 16;
    
    Septen's avatar
    Septen committed
                    xImage->depth = pixStride * 8;
                    xImage->bytes_per_line = stride;
                    xImage->bits_per_pixel = pixStride * 8;
    
    Josh Siegle's avatar
    Josh Siegle committed
                    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);
    
           #if JUCE_USE_XSHM
    
    Septen's avatar
    Septen committed
            if (isUsingXShm())
    
    Josh Siegle's avatar
    Josh Siegle committed
            {
                XShmDetach (display, &segmentInfo);
    
                XFlush (display);
                XDestroyImage (xImage);
    
                shmdt (segmentInfo.shmaddr);
                shmctl (segmentInfo.shmid, IPC_RMID, 0);
            }
            else
           #endif
            {
                xImage->data = nullptr;
                XDestroyImage (xImage);
            }
        }
    
        LowLevelGraphicsContext* createLowLevelContext() override
        {
            sendDataChangeMessage();
            return new LowLevelGraphicsSoftwareRenderer (Image (this));
        }
    
        void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
        {
            bitmap.data = imageData + x * pixelStride + y * lineStride;
            bitmap.pixelFormat = pixelFormat;
            bitmap.lineStride = lineStride;
            bitmap.pixelStride = pixelStride;
    
            if (mode != Image::BitmapData::readOnly)
                sendDataChangeMessage();
        }
    
        ImagePixelData* clone() override
        {
            jassertfalse;
            return nullptr;
        }
    
        ImageType* createType() const override     { return new NativeImageType(); }
    
    
    Septen's avatar
    Septen committed
        void blitToWindow (Window window, int dx, int dy, unsigned int dw, unsigned int dh, int sx, int sy)
    
    Josh Siegle's avatar
    Josh Siegle committed
        {
            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)
            {
    
    Septen's avatar
    Septen committed
                const uint32 rMask   = (uint32) xImage->red_mask;
                const uint32 gMask   = (uint32) xImage->green_mask;
                const uint32 bMask   = (uint32) xImage->blue_mask;
                const uint32 rShiftL = (uint32) jmax (0,  getShiftNeeded (rMask));
                const uint32 rShiftR = (uint32) jmax (0, -getShiftNeeded (rMask));
                const uint32 gShiftL = (uint32) jmax (0,  getShiftNeeded (gMask));
                const uint32 gShiftR = (uint32) jmax (0, -getShiftNeeded (gMask));
                const uint32 bShiftL = (uint32) jmax (0,  getShiftNeeded (bMask));
                const uint32 bShiftR = (uint32) jmax (0, -getShiftNeeded (bMask));
    
    Josh Siegle's avatar
    Josh Siegle committed
    
                const Image::BitmapData srcData (Image (this), Image::BitmapData::readOnly);
    
    
    Septen's avatar
    Septen committed
                for (int y = sy; y < sy + (int)dh; ++y)
    
    Josh Siegle's avatar
    Josh Siegle committed
                {
                    const uint8* p = srcData.getPixelPointer (sx, y);
    
    
    Septen's avatar
    Septen committed
                    for (int x = sx; x < sx + (int)dw; ++x)
    
    Josh Siegle's avatar
    Josh Siegle committed
                    {
                        const PixelRGB* const pixel = (const PixelRGB*) p;
                        p += srcData.pixelStride;
    
                        XPutPixel (xImage, x, y,
    
    Septen's avatar
    Septen committed
                                       (((((uint32) pixel->getRed())   << rShiftL) >> rShiftR) & rMask)
    
    Josh Siegle's avatar
    Josh Siegle committed
                                     | (((((uint32) pixel->getGreen()) << gShiftL) >> gShiftR) & gMask)
    
    Septen's avatar
    Septen committed
                                     | (((((uint32) pixel->getBlue())  << bShiftL) >> bShiftR) & bMask));
    
    Josh Siegle's avatar
    Josh Siegle committed
                    }
                }
            }
    
            // blit results to screen.
           #if JUCE_USE_XSHM
    
    Septen's avatar
    Septen committed
            if (isUsingXShm())
    
    Josh Siegle's avatar
    Josh Siegle committed
                XShmPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh, True);
            else
           #endif
                XPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh);
        }
    
    
    Septen's avatar
    Septen committed
        #if JUCE_USE_XSHM
        bool isUsingXShm() const noexcept       { return usingXShm; }
        #endif
    
    
    Josh Siegle's avatar
    Josh Siegle committed
    private:
        //==============================================================================
        XImage* xImage;
    
    Septen's avatar
    Septen committed
        const unsigned int imageDepth;
        HeapBlock<uint8> imageDataAllocated;
        HeapBlock<char> imageData16Bit;
    
    Josh Siegle's avatar
    Josh Siegle committed
        int pixelStride, lineStride;
        uint8* imageData;
        GC gc;
    
       #if JUCE_USE_XSHM
        XShmSegmentInfo segmentInfo;
        bool usingXShm;
       #endif
    
        static int getShiftNeeded (const uint32 mask) noexcept
        {
            for (int i = 32; --i >= 0;)
                if (((mask >> i) & 1) != 0)
                    return i - 7;
    
            jassertfalse;
            return 0;
        }
    
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XBitmapImage)
    };
    
    
    Septen's avatar
    Septen committed
    //==============================================================================
    #if JUCE_USE_XRANDR
    template <>
    struct ContainerDeletePolicy<XRRScreenResources>
    {
        static void destroy (XRRScreenResources* object);
    };
    
    template <>
    struct ContainerDeletePolicy<XRROutputInfo>
    {
        static void destroy (XRROutputInfo* object);
    };
    
    template <>
    struct ContainerDeletePolicy<XRRCrtcInfo>
    {
        static void destroy (XRRCrtcInfo* object);
    };
    #endif
    
    //==============================================================================
    class DisplayGeometry
    {
    private:
        //==============================================================================
        DisplayGeometry (::Display* dpy, double masterScale)
        {
            jassert (instance == nullptr);
            instance = this;
    
            queryDisplayInfos (dpy, masterScale);
            updatePositions();
        }
    
    public:
        //==============================================================================
        struct ExtendedInfo
        {
            // Unlike Desktop::Displays::Display, the following is in
            // physical pixels, i.e. the area is not scaled
            Rectangle<int> totalBounds;
            // Usable bounds is the usable area in local coordinates
            // with respect to the above totalBounds
            Rectangle<int> usableBounds;
            // top-left point of display in scaled coordinates. This
            // is different from totalBounds.getTopLeft() / scale,
            // because the neighbouring display may have a different
            // scale factor
            Point<int> topLeftScaled;
            double dpi, scale;
            bool isMain;
        };
    
        Array<ExtendedInfo> infos;
    
        //==============================================================================
        ExtendedInfo& findDisplayForRect (const Rectangle<int>& bounds, bool isScaledBounds)
        {
            int maxArea = -1;
            ExtendedInfo* retval = nullptr;
    
            for (int i = 0; i < infos.size(); ++i)
            {
                ExtendedInfo& dpy = infos.getReference (i);
    
                Rectangle<int> displayBounds = dpy.totalBounds;
    
                if (isScaledBounds)
                    displayBounds = (displayBounds.withZeroOrigin() / dpy.scale) + dpy.topLeftScaled;
    
                displayBounds = displayBounds.getIntersection (bounds);
                int area = displayBounds.getWidth() * displayBounds.getHeight();
    
                if (area >= maxArea)
                {
                    maxArea = area;
                    retval = &dpy;
                }
            }
    
            return *retval;
        }
    
        ExtendedInfo& findDisplayForPoint (Point<int> pt, bool isScaledPoint)
        {
            int minDistance = (int) ((((unsigned int)(-1)) >> 1) - 1);
            ExtendedInfo* retval = nullptr;
    
            for (int i = 0; i < infos.size(); ++i)
            {
                ExtendedInfo& dpy = infos.getReference (i);
    
                Rectangle<int> displayBounds = dpy.totalBounds;
    
                if (isScaledPoint)
                    displayBounds = (displayBounds.withZeroOrigin() / dpy.scale) + dpy.topLeftScaled;
    
                if (displayBounds.contains (pt))
                    return dpy;
    
                int distance = displayBounds.getCentre().getDistanceFrom (pt);
                if (distance <= minDistance)
                {
                    minDistance = distance;
                    retval = &dpy;
                }
            }
    
            return *retval;
        }
    
        //==============================================================================
        static Rectangle<int> physicalToScaled (const Rectangle<int>& physicalBounds)
        {
            // first find with which display physicalBounds has the most overlap
            ExtendedInfo& dpy = getInstance().findDisplayForRect (physicalBounds, false);
    
            // convert to local screen bounds
            Rectangle<int> retval = physicalBounds - dpy.totalBounds.getTopLeft();
    
            // now we can safely scale the coordinates and convert to global again
            return (retval / dpy.scale) + dpy.topLeftScaled;
        }
    
        static Rectangle<int> scaledToPhysical (const Rectangle<int>& scaledBounds)
        {
            // first find with which display physicalBounds has the most overlap
            ExtendedInfo& dpy = getInstance().findDisplayForRect (scaledBounds, true);
    
            // convert to local screen bounds
            Rectangle<int> retval = scaledBounds - dpy.topLeftScaled;
    
            // now we can safely scale the coordinates and convert to global again
            return (retval * dpy.scale) + dpy.totalBounds.getTopLeft();
        }
    
        //==============================================================================
        template <typename ValueType>
        static Point<ValueType> physicalToScaled (const Point<ValueType>& physicalPoint)
        {
            ExtendedInfo& dpy = getInstance().findDisplayForPoint (physicalPoint.roundToInt(), false);
            Point<ValueType> scaledTopLeft =
                Point<ValueType> (dpy.topLeftScaled.getX(), dpy.topLeftScaled.getY());
            Point<ValueType> physicalTopLeft =
                Point<ValueType> (dpy.totalBounds.getX(), dpy.totalBounds.getY());
    
            return ((physicalPoint - physicalTopLeft) / dpy.scale) + scaledTopLeft;
        }
    
        template <typename ValueType>
        static Point<ValueType> scaledToPhysical (const Point<ValueType>& scaledPoint)
        {
            ExtendedInfo& dpy = getInstance().findDisplayForPoint (scaledPoint.roundToInt(), true);
            Point<ValueType> scaledTopLeft =
                Point<ValueType> (dpy.topLeftScaled.getX(), dpy.topLeftScaled.getY());
            Point<ValueType> physicalTopLeft =
                Point<ValueType> (dpy.totalBounds.getX(), dpy.totalBounds.getY());
    
            return ((scaledPoint - scaledTopLeft) * dpy.scale) + physicalTopLeft;
        }
    
        //==============================================================================
        static DisplayGeometry& getInstance()
        {
            jassert (instance != nullptr);
            return *instance;
        }
    
        static DisplayGeometry& getOrCreateInstance (::Display* dpy, double masterScale)
        {
            if (instance == nullptr)
                new DisplayGeometry (dpy, masterScale);
    
            return getInstance();
        }
    
    private:
        //==============================================================================
        static DisplayGeometry* instance;
    
        //==============================================================================
       #if JUCE_USE_XINERAMA
        static Array<XineramaScreenInfo> XineramaQueryDisplays (::Display* dpy)
        {
            typedef Bool (*tXineramaIsActive) (::Display*);
            typedef XineramaScreenInfo* (*tXineramaQueryScreens) (::Display*, int*);
    
            int major_opcode, first_event, first_error;
    
            if (XQueryExtension (dpy, "XINERAMA", &major_opcode, &first_event, &first_error))
            {
                static void* libXinerama = nullptr;
                static tXineramaIsActive isActiveFuncPtr = nullptr;
                static tXineramaQueryScreens xineramaQueryScreens = nullptr;
    
                if (libXinerama == nullptr)
                {
                    libXinerama = dlopen ("libXinerama.so", RTLD_GLOBAL | RTLD_NOW);
    
                    if (libXinerama == nullptr)
                        libXinerama = dlopen ("libXinerama.so.1", RTLD_GLOBAL | RTLD_NOW);
    
                    if (libXinerama != nullptr)
                    {
                        isActiveFuncPtr = (tXineramaIsActive) dlsym (libXinerama, "XineramaIsActive");
                        xineramaQueryScreens = (tXineramaQueryScreens) dlsym (libXinerama, "XineramaQueryScreens");
                    }
                }
    
                if (isActiveFuncPtr != nullptr && xineramaQueryScreens != nullptr && isActiveFuncPtr (dpy) != 0)
                {
                    int numScreens;
                    if (XineramaScreenInfo* xinfo = xineramaQueryScreens (dpy, &numScreens))
                    {
                        Array<XineramaScreenInfo> infos (xinfo, numScreens);
                        XFree (xinfo);
    
                        return infos;
                    }
                }
            }
    
            return Array<XineramaScreenInfo>();
        }
       #endif
    
        //==============================================================================
       #if JUCE_USE_XRANDR
        friend struct ContainerDeletePolicy<XRRScreenResources>;
        friend struct ContainerDeletePolicy<XRROutputInfo>;
        friend struct ContainerDeletePolicy<XRRCrtcInfo>;
    
        class XRandrWrapper
        {
        private:
            XRandrWrapper()
                : libXrandr (nullptr),
                  getScreenResourcesPtr (nullptr),
                  freeScreenResourcesPtr (nullptr),
                  getOutputInfoPtr (nullptr),
                  freeOutputInfoPtr (nullptr),
                  getCrtcInfoPtr (nullptr),
                  freeCrtcInfoPtr (nullptr),
                  getOutputPrimaryPtr (nullptr)
            {
                if (libXrandr == nullptr)
                {
                    libXrandr = dlopen ("libXrandr.so", RTLD_GLOBAL | RTLD_NOW);
    
                    if (libXrandr == nullptr)
                        libXrandr = dlopen ("libXinerama.so.2", RTLD_GLOBAL | RTLD_NOW);
    
                    if (libXrandr != nullptr)
                    {
                        getScreenResourcesPtr  = (tXRRGetScreenResources)  dlsym (libXrandr, "XRRGetScreenResources");
                        freeScreenResourcesPtr = (tXRRFreeScreenResources) dlsym (libXrandr, "XRRFreeScreenResources");
                        getOutputInfoPtr       = (tXRRGetOutputInfo)       dlsym (libXrandr, "XRRGetOutputInfo");
                        freeOutputInfoPtr      = (tXRRFreeOutputInfo)      dlsym (libXrandr, "XRRFreeOutputInfo");
                        getCrtcInfoPtr         = (tXRRGetCrtcInfo)         dlsym (libXrandr, "XRRGetCrtcInfo");
                        freeCrtcInfoPtr        = (tXRRFreeCrtcInfo)        dlsym (libXrandr, "XRRFreeCrtcInfo");
                        getOutputPrimaryPtr    = (tXRRGetOutputPrimary)    dlsym (libXrandr, "XRRGetOutputPrimary");
                    }
                }