1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-15 00:24:19 +00:00

Tidied up some linux messaging code. Fixed a couple of small graphic bugs.

This commit is contained in:
Julian Storer 2010-05-30 12:56:29 +01:00
parent 0c541cfba2
commit 1baaa016bd
7 changed files with 493 additions and 565 deletions

View file

@ -48,7 +48,8 @@ class InternalMessageQueue
{
public:
InternalMessageQueue()
: bytesInSocket (0)
: bytesInSocket (0),
totalEventCount (0)
{
int ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd);
(void) ret; jassert (ret == 0);
@ -61,8 +62,11 @@ public:
{
close (fd[0]);
close (fd[1]);
clearSingletonInstance();
}
//==============================================================================
void postMessage (Message* msg)
{
const int maxBytesInSocketQueue = 128;
@ -87,6 +91,108 @@ public:
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();
else
return dispatchNextInternalMessage() || dispatchNextXEvent();
}
// Wait for an event (either XEvent, or an internal Message)
bool sleepUntilEvent (const int timeoutMs)
{
if (! 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 = 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
}
//==============================================================================
struct MessageThreadFuncCall
{
enum { uniqueID = 0x73774623 };
MessageCallbackFunction* func;
void* parameter;
void* result;
CriticalSection lock;
WaitableEvent event;
};
//==============================================================================
juce_DeclareSingleton_SingleThreaded_Minimal (InternalMessageQueue);
private:
CriticalSection lock;
OwnedArray <Message> queue;
int fd[2];
int bytesInSocket;
int totalEventCount;
int getWaitHandle() const throw() { 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 == 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;
}
Message* popNextMessage()
{
ScopedLock sl (lock);
@ -106,95 +212,103 @@ public:
return m;
}
int getWaitHandle() const { return fd[1]; }
private:
CriticalSection lock;
OwnedArray <Message> queue;
int fd[2];
int bytesInSocket;
static bool setNonBlocking (int handle)
bool dispatchNextInternalMessage()
{
int socketFlags = fcntl (handle, F_GETFL, 0);
ScopedPointer <Message> msg (popNextMessage());
if (socketFlags == -1)
if (msg == 0)
return false;
socketFlags |= O_NONBLOCK;
if (msg->intParameter1 == MessageThreadFuncCall::uniqueID)
{
// Handle callback message
MessageThreadFuncCall* const call = (MessageThreadFuncCall*) msg->pointerParameter;
return fcntl (handle, F_SETFL, socketFlags) == 0;
call->result = (*(call->func)) (call->parameter);
call->event.signal();
}
else
{
// Handle "normal" messages
MessageManager::getInstance()->deliverMessage (msg.release());
}
return true;
}
};
juce_ImplementSingleton_SingleThreaded (InternalMessageQueue);
//==============================================================================
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)
namespace LinuxErrorHandling
{
DBG ("ERROR: connection to X server broken.. terminating.");
//==============================================================================
static bool errorOccurred = false;
static bool keyboardBreakOccurred = false;
static XErrorHandler oldErrorHandler = (XErrorHandler) 0;
static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) 0;
errorOccurred = true;
if (JUCEApplication::getInstance() != 0)
Process::terminate();
return 0;
}
// A protocol error has occurred
static int errorHandler (Display* display, XErrorEvent* 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).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)
//==============================================================================
// Usually happens when client-server connection is broken
static int ioErrorHandler (Display* display)
{
keyboardBreakOccurred = true;
return;
}
static bool reentrant = false;
if (! reentrant)
{
reentrant = true;
// Illegal instruction
fflush (stdout);
Logger::outputDebugString ("ERROR: Program executed illegal instruction.. terminating");
DBG ("ERROR: connection to X server broken.. terminating.");
errorOccurred = true;
if (JUCEApplication::getInstance() != 0)
Process::terminate();
return 0;
}
else
// A protocol error has occurred
static int juce_XErrorHandler (Display* display, XErrorEvent* event)
{
if (JUCEApplication::getInstance() != 0)
exit(0);
#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).toCString(), "Unknown", requestStr, 64);
DBG ("ERROR: X returned " + String (errorStr) + " for operation " + String (requestStr));
#endif
return 0;
}
static void installXErrorHandlers()
{
oldIOErrorHandler = XSetIOErrorHandler (ioErrorHandler);
oldErrorHandler = XSetErrorHandler (juce_XErrorHandler);
}
static void removeXErrorHandlers()
{
XSetIOErrorHandler (oldIOErrorHandler);
oldIOErrorHandler = 0;
XSetErrorHandler (oldErrorHandler);
oldErrorHandler = 0;
}
//==============================================================================
static void keyboardBreakSignalHandler (int sig)
{
if (sig == SIGINT)
keyboardBreakOccurred = true;
}
static 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);
}
}
@ -220,32 +334,11 @@ void MessageManager::doPlatformSpecificInitialisation()
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, 0);
#ifndef _DEBUG
// Setup signal handlers for various fatal errors
sigaction (SIGILL, &saction, 0);
sigaction (SIGBUS, &saction, 0);
sigaction (SIGFPE, &saction, 0);
sigaction (SIGSEGV, &saction, 0);
sigaction (SIGSYS, &saction, 0);
#endif
LinuxErrorHandling::installXErrorHandlers();
LinuxErrorHandling::installKeyboardBreakHandler();
// Create the internal message queue
juce_internalMessageQueue = new InternalMessageQueue();
InternalMessageQueue::getInstance();
// Try to connect to a display
String displayName (getenv ("DISPLAY"));
@ -254,59 +347,46 @@ void MessageManager::doPlatformSpecificInitialisation()
display = XOpenDisplay (displayName.toCString());
if (display == 0)
if (display != 0) // This is not fatal! we can run headless.
{
// This is not fatal! we can run headless.
return;
// Create a context to store user data associated with Windows we create in WindowDriver
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);
}
// 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
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)
juce_messageWindowHandle = XCreateWindow (display, root,
0, 0, 1, 1, 0, 0, InputOnly,
visual, CWEventMask, &swa);
}
void MessageManager::doPlatformSpecificShutdown()
{
deleteAndZero (juce_internalMessageQueue);
InternalMessageQueue::deleteInstance();
if (display != 0 && ! errorOccurred)
if (display != 0 && ! LinuxErrorHandling::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;
LinuxErrorHandling::removeXErrorHandlers();
}
}
bool juce_postMessageToSystemQueue (void* message)
{
if (errorOccurred)
if (LinuxErrorHandling::errorOccurred)
return false;
juce_internalMessageQueue->postMessage ((Message*) message);
InternalMessageQueue::getInstanceWithoutCreating()->postMessage ((Message*) message);
return true;
}
@ -315,144 +395,37 @@ void MessageManager::broadcastMessage (const String& value) throw()
/* TODO */
}
struct MessageThreadFuncCall
{
enum { uniqueID = 0x73774623 };
MessageCallbackFunction* func;
void* parameter;
void* result;
CriticalSection lock;
WaitableEvent event;
};
void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func,
void* parameter)
{
if (errorOccurred)
if (LinuxErrorHandling::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
if (isThisTheMessageThread())
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;
InternalMessageQueue::MessageThreadFuncCall messageCallContext;
messageCallContext.func = func;
messageCallContext.parameter = parameter;
if (display != 0)
{
ScopedXLock xlock;
if (XPending (display))
return true;
}
InternalMessageQueue::getInstanceWithoutCreating()
->postMessage (new Message (InternalMessageQueue::MessageThreadFuncCall::uniqueID,
0, 0, &messageCallContext));
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = timeoutMs * 1000;
int fd0 = juce_internalMessageQueue->getWaitHandle();
int fdmax = fd0;
// Wait for it to complete before continuing
messageCallContext.event.wait();
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;
return messageCallContext.result;
}
// this function expects that it will NEVER be called simultaneously for two concurrent threads
bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
{
for (;;)
while (! LinuxErrorHandling::errorOccurred)
{
if (errorOccurred)
break;
if (keyboardBreakOccurred)
if (LinuxErrorHandling::keyboardBreakOccurred)
{
errorOccurred = true;
LinuxErrorHandling::errorOccurred = true;
if (JUCEApplication::getInstance() != 0)
Process::terminate();
@ -460,28 +433,13 @@ bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
break;
}
static int totalEventCount = 0;
++totalEventCount;
if (InternalMessageQueue::getInstanceWithoutCreating()->dispatchNextEvent())
return true;
// 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
if (returnIfNoPendingMessages)
break;
// the timeout is to be on the safe side, but it does not seem to be useful
juce_sleepUntilEvent (2000);
InternalMessageQueue::getInstanceWithoutCreating()->sleepUntilEvent (2000);
}
return false;