|  | /*
  ==============================================================================
   This file is part of the JUCE library.
   Copyright (c) 2015 - 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.
  ==============================================================================
*/
#if JUCE_DEBUG && ! defined (JUCE_DEBUG_XERRORS)
 #define JUCE_DEBUG_XERRORS 1
#endif
Display* display = nullptr;
Window juce_messageWindowHandle = None;
XContext windowHandleXContext;   // This is referenced from Windowing.cpp
typedef bool (*WindowMessageReceiveCallback) (XEvent&);
WindowMessageReceiveCallback dispatchWindowMessage = nullptr;
typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&);
SelectionRequestCallback handleSelectionRequest = nullptr;
//==============================================================================
ScopedXLock::ScopedXLock()       { if (display != nullptr) XLockDisplay (display); }
ScopedXLock::~ScopedXLock()      { if (display != nullptr) XUnlockDisplay (display); }
//==============================================================================
class InternalMessageQueue
{
public:
    InternalMessageQueue()
        : bytesInSocket (0),
          totalEventCount (0)
    {
        int ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd);
        ignoreUnused (ret); jassert (ret == 0);
    }
    ~InternalMessageQueue()
    {
        close (fd[0]);
        close (fd[1]);
        clearSingletonInstance();
    }
    //==============================================================================
    void postMessage (MessageManager::MessageBase* const msg)
    {
        const int maxBytesInSocketQueue = 128;
        ScopedLock sl (lock);
        queue.add (msg);
        if (bytesInSocket < maxBytesInSocketQueue)
        {
            ++bytesInSocket;
            ScopedUnlock ul (lock);
            const unsigned char x = 0xff;
            ssize_t bytesWritten = write (fd[0], &x, 1);
            ignoreUnused (bytesWritten);
        }
    }
    bool isEmpty() const
    {
        ScopedLock sl (lock);
        return queue.size() == 0;
    }
    bool dispatchNextEvent()
    {
        // This alternates between giving priority to XEvents or internal messages,
        // to keep everything running smoothly..
        if ((++totalEventCount & 1) != 0)
            return dispatchNextXEvent() || dispatchNextInternalMessage();
        return dispatchNextInternalMessage() || dispatchNextXEvent();
    }
    // Wait for an event (either XEvent, or an internal Message)
    bool sleepUntilEvent (const int timeoutMs)
    {
        if (! isEmpty())
            return true;
        if (display != nullptr)
        {
            ScopedXLock xlock;
            if (XPending (display))
                return true;
        }
        struct timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec = timeoutMs * 1000;
        int fd0 = getWaitHandle();
        int fdmax = fd0;
        fd_set readset;
        FD_ZERO (&readset);
        FD_SET (fd0, &readset);
        if (display != nullptr)
        {
            ScopedXLock xlock;
            int fd1 = XConnectionNumber (display);
            FD_SET (fd1, &readset);
            fdmax = jmax (fd0, fd1);
        }
        const int ret = select (fdmax + 1, &readset, 0, 0, &tv);
        return (ret > 0); // ret <= 0 if error or timeout
    }
    //==============================================================================
    juce_DeclareSingleton_SingleThreaded_Minimal (InternalMessageQueue)
private:
    CriticalSection lock;
    ReferenceCountedArray <MessageManager::MessageBase> queue;
    int fd[2];
    int bytesInSocket;
    int totalEventCount;
    int getWaitHandle() const noexcept      { return fd[1]; }
    static bool setNonBlocking (int handle)
    {
        int socketFlags = fcntl (handle, F_GETFL, 0);
        if (socketFlags == -1)
            return false;
        socketFlags |= O_NONBLOCK;
        return fcntl (handle, F_SETFL, socketFlags) == 0;
    }
    static bool dispatchNextXEvent()
    {
        if (display == nullptr)
            return false;
        XEvent evt;
        {
            ScopedXLock xlock;
            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);
        return true;
    }
    MessageManager::MessageBase::Ptr popNextMessage()
    {
        const ScopedLock sl (lock);
        if (bytesInSocket > 0)
        {
            --bytesInSocket;
            const ScopedUnlock ul (lock);
            unsigned char x;
            ssize_t numBytes = read (fd[1], &x, 1);
            ignoreUnused (numBytes);
        }
        return queue.removeAndReturn (0);
    }
    bool dispatchNextInternalMessage()
    {
        if (const MessageManager::MessageBase::Ptr msg = popNextMessage())
        {
            JUCE_TRY
            {
                msg->messageCallback();
                return true;
            }
            JUCE_CATCH_EXCEPTION
        }
        return false;
    }
};
juce_ImplementSingleton_SingleThreaded (InternalMessageQueue)
//==============================================================================
namespace LinuxErrorHandling
{
    static bool errorOccurred = false;
    static bool keyboardBreakOccurred = false;
    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();
        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()
    {
        if (JUCEApplicationBase::isStandaloneApp())
        {
            XSetIOErrorHandler (oldIOErrorHandler);
            oldIOErrorHandler = 0;
            XSetErrorHandler (oldErrorHandler);
            oldErrorHandler = 0;
        }
    }
    //==============================================================================
    void keyboardBreakSignalHandler (int sig)
    {
        if (sig == SIGINT)
            keyboardBreakOccurred = true;
    }
    void installKeyboardBreakHandler()
    {
        struct sigaction saction;
        sigset_t maskSet;
        sigemptyset (&maskSet);
        saction.sa_handler = keyboardBreakSignalHandler;
        saction.sa_mask = maskSet;
        saction.sa_flags = 0;
        sigaction (SIGINT, &saction, 0);
    }
}
//==============================================================================
void MessageManager::doPlatformSpecificInitialisation()
{
    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;
        }
        LinuxErrorHandling::installXErrorHandlers();
        LinuxErrorHandling::installKeyboardBreakHandler();
    }
    // Create the internal message queue
    InternalMessageQueue::getInstance();
    // Try to connect to a display
    String displayName (getenv ("DISPLAY"));
    if (displayName.isEmpty())
        displayName = ":0.0";
    display = XOpenDisplay (displayName.toUTF8());
    if (display != nullptr)  // This is not fatal! we can run headless.
    {
        // 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);
    }
}
void MessageManager::doPlatformSpecificShutdown()
{
    InternalMessageQueue::deleteInstance();
    if (display != nullptr && ! LinuxErrorHandling::errorOccurred)
    {
        XDestroyWindow (display, juce_messageWindowHandle);
        juce_messageWindowHandle = 0;
        display = nullptr;
        LinuxErrorHandling::removeXErrorHandlers();
    }
}
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
{
    if (! LinuxErrorHandling::errorOccurred)
    {
        if (InternalMessageQueue* queue = InternalMessageQueue::getInstanceWithoutCreating())
        {
            queue->postMessage (message);
            return true;
        }
    }
    return false;
}
void MessageManager::broadcastMessage (const String& /* value */)
{
    /* TODO */
}
// this function expects that it will NEVER be called simultaneously for two concurrent threads
bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
{
    while (! LinuxErrorHandling::errorOccurred)
    {
        if (LinuxErrorHandling::keyboardBreakOccurred)
        {
            LinuxErrorHandling::errorOccurred = true;
            if (JUCEApplicationBase::isStandaloneApp())
                Process::terminate();
            break;
        }
        if (InternalMessageQueue* queue = InternalMessageQueue::getInstanceWithoutCreating())
        {
            if (queue->dispatchNextEvent())
                return true;
            if (returnIfNoPendingMessages)
                break;
            queue->sleepUntilEvent (2000);
        }
    }
    return false;
}
 |