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:
parent
0c541cfba2
commit
1baaa016bd
7 changed files with 493 additions and 565 deletions
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue