|  | /*
  ==============================================================================
   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.
   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.
  ==============================================================================
*/
typedef void (*WindowMessageReceiveCallback) (XEvent&);
WindowMessageReceiveCallback dispatchWindowMessage = nullptr;
typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&);
SelectionRequestCallback handleSelectionRequest = nullptr;
::Window juce_messageWindowHandle;
XContext windowHandleXContext;
//==============================================================================
namespace X11ErrorHandling
{
    static XErrorHandler oldErrorHandler = (XErrorHandler) 0;
    static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) 0;
    //==============================================================================
    // Usually happens when client-server connection is broken
    int ioErrorHandler (::Display*)
    {
        DBG ("ERROR: connection to X server broken.. terminating.");
        if (JUCEApplicationBase::isStandaloneApp())
            MessageManager::getInstance()->stopDispatchLoop();
        // set this somewhere
        // errorOccurred = true;
        return 0;
    }
    int errorHandler (::Display* display, XErrorEvent* event)
    {
        ignoreUnused (display, event);
       #if JUCE_DEBUG_XERRORS
        char errorStr[64] = { 0 };
        char requestStr[64] = { 0 };
        XGetErrorText (display, event->error_code, errorStr, 64);
        XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toUTF8(), "Unknown", requestStr, 64);
        DBG ("ERROR: X returned " << errorStr << " for operation " << requestStr);
       #endif
        return 0;
    }
    void installXErrorHandlers()
    {
        oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler);
        oldErrorHandler = XSetErrorHandler (errorHandler);
    }
    void removeXErrorHandlers()
    {
        XSetIOErrorHandler (oldIOErrorHandler);
        oldIOErrorHandler = 0;
        XSetErrorHandler (oldErrorHandler);
        oldErrorHandler = 0;
    }
}
//==============================================================================
XWindowSystem::XWindowSystem() noexcept
    : display (nullptr)
{
    if (JUCEApplicationBase::isStandaloneApp())
    {
        // Initialise xlib for multiple thread support
        static bool initThreadCalled = false;
        if (! initThreadCalled)
        {
            if (! XInitThreads())
            {
                // This is fatal!  Print error and closedown
                Logger::outputDebugString ("Failed to initialise xlib thread support.");
                Process::terminate();
                return;
            }
            initThreadCalled = true;
        }
        X11ErrorHandling::installXErrorHandlers();
    }
}
XWindowSystem::~XWindowSystem() noexcept
{
    if (JUCEApplicationBase::isStandaloneApp())
        X11ErrorHandling::removeXErrorHandlers();
    clearSingletonInstance();
}
::Display* XWindowSystem::displayRef() noexcept
{
    if (++displayCount - 1 == 0)
    {
        String displayName (getenv ("DISPLAY"));
        if (displayName.isEmpty())
            displayName = ":0.0";
        display = XOpenDisplay (displayName.toUTF8());
        initialiseXDisplay();
    }
    return this->display;
}
::Display* XWindowSystem::displayUnref() noexcept
{
    jassert (display != nullptr);
    jassert (displayCount.get() > 0);
    if (--displayCount == 0)
    {
        destroyXDisplay();
        XCloseDisplay (display);
        display = nullptr;
    }
    return display;
}
void XWindowSystem::initialiseXDisplay() noexcept
{
    // This is fatal!  Print error and closedown
    if (display == nullptr)
    {
        Logger::outputDebugString ("Failed to connect to the X Server.");
        Process::terminate();
    }
    // Create a context to store user data associated with Windows we create
    windowHandleXContext = XUniqueContext();
    // We're only interested in client messages for this window, which are always sent
    XSetWindowAttributes swa;
    swa.event_mask = NoEventMask;
    // Create our message window (this will never be mapped)
    const int screen = DefaultScreen (display);
    juce_messageWindowHandle = XCreateWindow (display, RootWindow (display, screen),
                                              0, 0, 1, 1, 0, 0, InputOnly,
                                              DefaultVisual (display, screen),
                                              CWEventMask, &swa);
    XSync (display, False);
    // Setup input event handler
    int fd = XConnectionNumber (display);
    LinuxEventLoop::setWindowSystemFd
        (fd,
         [this](int /*fd*/) {
            do
            {
                XEvent evt;
                {
                    ScopedXLock xlock (display);
                    if (! XPending (display))
                        return false;
                    XNextEvent (display, &evt);
                }
                if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle
                    && handleSelectionRequest != nullptr)
                    handleSelectionRequest (evt.xselectionrequest);
                else if (evt.xany.window != juce_messageWindowHandle
                         && dispatchWindowMessage != nullptr)
                    dispatchWindowMessage (evt);
            } while (display != nullptr);
            return false;
        });
}
void XWindowSystem::destroyXDisplay() noexcept
{
    ScopedXLock xlock (display);
    XDestroyWindow (display, juce_messageWindowHandle);
    juce_messageWindowHandle = 0;
    XSync (display, True);
    LinuxEventLoop::removeWindowSystemFd();
}
juce_ImplementSingleton (XWindowSystem)
//==============================================================================
ScopedXDisplay::ScopedXDisplay()
{
    display = XWindowSystem::getInstance()->displayRef();
}
ScopedXDisplay::~ScopedXDisplay()
{
    XWindowSystem::getInstance()->displayUnref();
}
::Display* ScopedXDisplay::get()
{
    return display;
}
//==============================================================================
ScopedXLock::ScopedXLock(::Display* _display)
    : display (_display)
{
    if (display != nullptr) XLockDisplay (display);
}
ScopedXLock::~ScopedXLock()
{
    if (display != nullptr) XUnlockDisplay (display);
}
//==============================================================================
Atoms::Atoms(::Display* display)
{
    protocols                    = getIfExists (display, "WM_PROTOCOLS");
    protocolList [TAKE_FOCUS]    = getIfExists (display, "WM_TAKE_FOCUS");
    protocolList [DELETE_WINDOW] = getIfExists (display, "WM_DELETE_WINDOW");
    protocolList [PING]          = getIfExists (display, "_NET_WM_PING");
    changeState                  = getIfExists (display, "WM_CHANGE_STATE");
    state                        = getIfExists (display, "WM_STATE");
    userTime                     = getCreating (display, "_NET_WM_USER_TIME");
    activeWin                    = getCreating (display, "_NET_ACTIVE_WINDOW");
    pid                          = getCreating (display, "_NET_WM_PID");
    windowType                   = getIfExists (display, "_NET_WM_WINDOW_TYPE");
    windowState                  = getIfExists (display, "_NET_WM_STATE");
    XdndAware                    = getCreating (display, "XdndAware");
    XdndEnter                    = getCreating (display, "XdndEnter");
    XdndLeave                    = getCreating (display, "XdndLeave");
    XdndPosition                 = getCreating (display, "XdndPosition");
    XdndStatus                   = getCreating (display, "XdndStatus");
    XdndDrop                     = getCreating (display, "XdndDrop");
    XdndFinished                 = getCreating (display, "XdndFinished");
    XdndSelection                = getCreating (display, "XdndSelection");
    XdndTypeList                 = getCreating (display, "XdndTypeList");
    XdndActionList               = getCreating (display, "XdndActionList");
    XdndActionCopy               = getCreating (display, "XdndActionCopy");
    XdndActionPrivate            = getCreating (display, "XdndActionPrivate");
    XdndActionDescription        = getCreating (display, "XdndActionDescription");
    allowedMimeTypes[0]          = getCreating (display, "UTF8_STRING");
    allowedMimeTypes[1]          = getCreating (display, "text/plain;charset=utf-8");
    allowedMimeTypes[2]          = getCreating (display, "text/plain");
    allowedMimeTypes[3]          = getCreating (display, "text/uri-list");
    allowedActions[0]            = getCreating (display, "XdndActionMove");
    allowedActions[1]            = XdndActionCopy;
    allowedActions[2]            = getCreating (display, "XdndActionLink");
    allowedActions[3]            = getCreating (display, "XdndActionAsk");
    allowedActions[4]            = XdndActionPrivate;
}
Atom Atoms::getIfExists (::Display* display, const char* name)
{
    return XInternAtom (display, name, True);
}
Atom Atoms::getCreating (::Display* display, const char* name)
{
    return XInternAtom (display, name, False);
}
String Atoms::getName (::Display* display, const Atom atom)
{
    if (atom == None)
        return "None";
    return String (XGetAtomName (display, atom));
}
bool Atoms::isMimeTypeFile (::Display* display, const Atom atom)
{
    return getName (display, atom).equalsIgnoreCase ("text/uri-list");
}
const unsigned long Atoms::DndVersion = 3;
//==============================================================================
GetXProperty::GetXProperty (::Display* display, 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::~GetXProperty()
{
    if (data != nullptr)
        XFree (data);
}
 |