1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-11 23:54:18 +00:00
JUCE/src/native/linux/juce_linux_Messaging.cpp
2010-03-23 17:36:12 +00:00

492 lines
13 KiB
C++

/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-9 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.
==============================================================================
*/
// (This file gets included by juce_linux_NativeCode.cpp, rather than being
// compiled on its own).
#if JUCE_INCLUDED_FILE
//==============================================================================
#ifdef JUCE_DEBUG
#define JUCE_DEBUG_XERRORS 1
#endif
Display* display = 0; // This is also referenced from WindowDriver.cpp
Window juce_messageWindowHandle = None;
XContext improbableNumber; // This is referenced from Windowing.cpp
extern void juce_windowMessageReceive (XEvent* event); // Defined in Windowing.cpp
extern void juce_handleSelectionRequest (XSelectionRequestEvent &evt); // Defined in Clipboard.cpp
//==============================================================================
ScopedXLock::ScopedXLock() { XLockDisplay (display); }
ScopedXLock::~ScopedXLock() { XUnlockDisplay (display); }
//==============================================================================
class InternalMessageQueue
{
public:
InternalMessageQueue()
: bytesInSocket (0)
{
int ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd);
(void) ret; jassert (ret == 0);
//setNonBlocking (fd[0]);
//setNonBlocking (fd[1]);
}
~InternalMessageQueue()
{
close (fd[0]);
close (fd[1]);
}
void postMessage (Message* msg)
{
const int maxBytesInSocketQueue = 128;
ScopedLock sl (lock);
queue.add (msg);
if (bytesInSocket < maxBytesInSocketQueue)
{
++bytesInSocket;
ScopedUnlock ul (lock);
const unsigned char x = 0xff;
size_t bytesWritten = write (fd[0], &x, 1);
(void) bytesWritten;
}
}
bool isEmpty() const
{
ScopedLock sl (lock);
return queue.size() == 0;
}
Message* popNextMessage()
{
ScopedLock sl (lock);
if (bytesInSocket > 0)
{
--bytesInSocket;
ScopedUnlock ul (lock);
unsigned char x;
size_t numBytes = read (fd[1], &x, 1);
(void) numBytes;
}
Message* m = queue[0];
queue.remove (0, false /* deleteObject */);
return m;
}
int getWaitHandle() const { return fd[1]; }
private:
CriticalSection lock;
OwnedArray <Message> queue;
int fd[2];
int bytesInSocket;
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;
}
};
//==============================================================================
struct MessageThreadFuncCall
{
enum { uniqueID = 0x73774623 };
MessageCallbackFunction* func;
void* parameter;
void* result;
CriticalSection lock;
WaitableEvent event;
};
//==============================================================================
static InternalMessageQueue* juce_internalMessageQueue = 0;
// error handling in X11
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
static int ioErrorHandler (Display* display)
{
DBG ("ERROR: connection to X server broken.. terminating.");
errorOccurred = true;
if (JUCEApplication::getInstance() != 0)
Process::terminate();
return 0;
}
// A protocol error has occurred
static int errorHandler (Display* display, XErrorEvent* event)
{
#ifdef 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).toCString(),
"Unknown", requestStr, 64);
DBG ("ERROR: X returned " + String (errorStr) + " for operation " + String (requestStr));
#endif
return 0;
}
// Breakin from keyboard
static void signalHandler (int sig)
{
if (sig == SIGINT)
{
keyboardBreakOccurred = true;
return;
}
static bool reentrant = false;
if (! reentrant)
{
reentrant = true;
// Illegal instruction
fflush (stdout);
Logger::outputDebugString ("ERROR: Program executed illegal instruction.. terminating");
errorOccurred = true;
if (JUCEApplication::getInstance() != 0)
Process::terminate();
}
else
{
if (JUCEApplication::getInstance() != 0)
exit(0);
}
}
//==============================================================================
void MessageManager::doPlatformSpecificInitialisation()
{
// 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.");
if (JUCEApplication::getInstance() != 0)
Process::terminate();
return;
}
initThreadCalled = true;
}
// This is called if the client/server connection is broken
oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler);
// This is called if a protocol error occurs
oldErrorHandler = XSetErrorHandler (errorHandler);
// Install signal handler for break-in
struct sigaction saction;
sigset_t maskSet;
sigemptyset (&maskSet);
saction.sa_handler = signalHandler;
saction.sa_mask = maskSet;
saction.sa_flags = 0;
sigaction (SIGINT, &saction, NULL);
#ifndef _DEBUG
// Setup signal handlers for various fatal errors
sigaction (SIGILL, &saction, NULL);
sigaction (SIGBUS, &saction, NULL);
sigaction (SIGFPE, &saction, NULL);
sigaction (SIGSEGV, &saction, NULL);
sigaction (SIGSYS, &saction, NULL);
#endif
// Create the internal message queue
juce_internalMessageQueue = new InternalMessageQueue();
// Try to connect to a display
String displayName (getenv ("DISPLAY"));
if (displayName.isEmpty())
displayName = ":0.0";
display = XOpenDisplay (displayName.toCString());
if (display == 0)
{
// This is not fatal! we can run headless.
return;
}
// Get defaults for various properties
int screen = DefaultScreen (display);
Window root = RootWindow (display, screen);
Visual* visual = DefaultVisual (display, screen);
// Create a context to store user data associated with Windows we
// create in WindowDriver
improbableNumber = 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)
juce_messageWindowHandle = XCreateWindow (display, root,
0, 0, 1, 1, 0, 0, InputOnly,
visual, CWEventMask, &swa);
}
void MessageManager::doPlatformSpecificShutdown()
{
deleteAndZero (juce_internalMessageQueue);
if (display != 0 && ! errorOccurred)
{
XDestroyWindow (display, juce_messageWindowHandle);
XCloseDisplay (display);
// reset pointers
juce_messageWindowHandle = 0;
display = 0;
// Restore original error handlers
XSetIOErrorHandler (oldIOErrorHandler);
oldIOErrorHandler = 0;
XSetErrorHandler (oldErrorHandler);
oldErrorHandler = 0;
}
}
bool juce_postMessageToSystemQueue (void* message)
{
if (errorOccurred)
return false;
juce_internalMessageQueue->postMessage ((Message*) message);
return true;
}
void MessageManager::broadcastMessage (const String& value) throw()
{
/* TODO */
}
void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func,
void* parameter)
{
if (errorOccurred)
return 0;
if (! isThisTheMessageThread())
{
MessageThreadFuncCall messageCallContext;
messageCallContext.func = func;
messageCallContext.parameter = parameter;
juce_internalMessageQueue->postMessage (new Message (MessageThreadFuncCall::uniqueID,
0, 0, &messageCallContext));
// Wait for it to complete before continuing
messageCallContext.event.wait();
return messageCallContext.result;
}
else
{
// Just call the function directly
return func (parameter);
}
}
// Wait for an event (either XEvent, or an internal Message)
static bool juce_sleepUntilEvent (const int timeoutMs)
{
if (! juce_internalMessageQueue->isEmpty())
return true;
if (display != 0)
{
ScopedXLock xlock;
if (XPending (display))
return true;
}
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = timeoutMs * 1000;
int fd0 = juce_internalMessageQueue->getWaitHandle();
int fdmax = fd0;
fd_set readset;
FD_ZERO (&readset);
FD_SET (fd0, &readset);
if (display != 0)
{
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
}
// Handle next XEvent (if any)
static bool juce_dispatchNextXEvent()
{
if (display == 0)
return false;
XEvent evt;
{
ScopedXLock xlock;
if (! XPending (display))
return false;
XNextEvent (display, &evt);
}
if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle)
{
juce_handleSelectionRequest (evt.xselectionrequest);
}
else if (evt.xany.window != juce_messageWindowHandle)
{
juce_windowMessageReceive (&evt);
}
return true;
}
// Handle next internal Message (if any)
static bool juce_dispatchNextInternalMessage()
{
ScopedPointer <Message> msg (juce_internalMessageQueue->popNextMessage());
if (msg == 0)
return false;
if (msg->intParameter1 == MessageThreadFuncCall::uniqueID)
{
// Handle callback message
MessageThreadFuncCall* const call = (MessageThreadFuncCall*) msg->pointerParameter;
call->result = (*(call->func)) (call->parameter);
call->event.signal();
}
else
{
// Handle "normal" messages
MessageManager::getInstance()->deliverMessage (msg.release());
}
return true;
}
// this function expects that it will NEVER be called simultaneously for two concurrent threads
bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
{
for (;;)
{
if (errorOccurred)
break;
if (keyboardBreakOccurred)
{
errorOccurred = true;
if (JUCEApplication::getInstance() != 0)
Process::terminate();
break;
}
static int totalEventCount = 0;
++totalEventCount;
// The purpose here is to give either priority to XEvents or
// to internal messages This is necessary to keep a "good"
// behaviour when the cpu is overloaded
if (totalEventCount & 1)
{
if (juce_dispatchNextXEvent() || juce_dispatchNextInternalMessage())
return true;
}
else
{
if (juce_dispatchNextInternalMessage() || juce_dispatchNextXEvent())
return true;
}
if (returnIfNoPendingMessages) // early exit
break;
// the timeout is to be on the safe side, but it does not seem to be useful
juce_sleepUntilEvent (2000);
}
return false;
}
#endif