|
- /*
- ==============================================================================
-
- 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;
- }
|