i’ve discovered the messaging code was concepted to work with standalone applications but not as plugins. here is a new version of the juce_linux_Messaging.cpp that fixes a couple of things when using juce as a plugin (or you prefer a patch ?).
i’ve added:
- safe guard XInitThread calls, can’t be called more than once if your plugin is opened and closed multiple times
- restore original XErrorHandlers on PlatformSpecificShutdown
- clear display and message window on PlatformSpecificShutdown
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-7 by Raw Material Software ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the
GNU General Public License, as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with JUCE; if not, visit www.gnu.org/licenses or write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
------------------------------------------------------------------------------
If you'd like to release a closed-source product which uses JUCE, commercial
licenses are also available: visit www.rawmaterialsoftware.com/juce for
more information.
==============================================================================
*/
#include "../../../juce_Config.h"
#if JUCE_BUILD_GUI_CLASSES
#include "linuxincludes.h"
#include <stdio.h>
#include <signal.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xresource.h>
#include <X11/Xutil.h>
#include "../../../src/juce_core/basics/juce_StandardHeader.h"
BEGIN_JUCE_NAMESPACE
#include "../../../src/juce_appframework/events/juce_MessageManager.h"
#include "../../../src/juce_core/threads/juce_WaitableEvent.h"
#include "../../../src/juce_core/threads/juce_Process.h"
#include "../../../src/juce_core/threads/juce_ScopedLock.h"
#ifdef JUCE_DEBUG
#define JUCE_DEBUG_XERRORS 1
#endif
Display* display = 0; // This is also referenced from WindowDriver.cpp
static Window juce_messageWindowHandle = None;
#define SpecialAtom "JUCESpecialAtom"
#define BroadcastAtom "JUCEBroadcastAtom"
#define SpecialCallbackAtom "JUCESpecialCallbackAtom"
static Atom specialId;
static Atom broadcastId;
static Atom specialCallbackId;
// This is referenced from WindowDriver.cpp
XContext improbableNumber;
// Defined in WindowDriver.cpp
extern void juce_windowMessageReceive (XEvent* event);
struct MessageThreadFuncCall
{
MessageCallbackFunction* func;
void* parameter;
void* result;
CriticalSection lock;
WaitableEvent event;
};
static bool errorCondition = false;
static XErrorHandler oldErrorHandler = (XErrorHandler) NULL ;
static XIOErrorHandler oldIOErrorHandler = (XIOErrorHandler) NULL ;
// (defined in another file to avoid problems including certain headers in this one)
extern bool juce_isRunningAsApplication();
// Usually happens when client-server connection is broken
static int ioErrorHandler (Display* display)
{
DBG (T("ERROR: connection to X server broken.. terminating."));
errorCondition = true;
if (juce_isRunningAsApplication())
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",
(const char*) String (event->request_code),
"Unknown",
requestStr,
64);
DBG (T("ERROR: X returned ") + String (errorStr) + T(" for operation ") + String (requestStr));
#endif
return 0;
}
static bool breakIn = false;
// Breakin from keyboard
static void sig_handler (int sig)
{
if (sig == SIGINT)
{
breakIn = true;
return;
}
static bool reentrant = false;
if (reentrant == false)
{
reentrant = true;
// Illegal instruction
fflush (stdout);
Logger::outputDebugString ("ERROR: Program executed illegal instruction.. terminating");
errorCondition = true;
if (juce_isRunningAsApplication())
Process::terminate();
}
else
{
if (juce_isRunningAsApplication())
exit(0);
}
}
static bool initThreadCalled = false;
//==============================================================================
void MessageManager::doPlatformSpecificInitialisation()
{
// Initialise xlib for multiple thread support
if (! initThreadCalled)
{
if (! XInitThreads())
{
// This is fatal! Print error and closedown
Logger::outputDebugString ("Failed to initialise xlib thread support.");
if (juce_isRunningAsApplication())
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 = sig_handler;
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
String displayName (getenv ("DISPLAY"));
if (displayName.isEmpty())
displayName = T(":0.0");
display = XOpenDisplay (displayName);
if (display == 0)
{
// This is fatal! Print error and closedown
Logger::outputDebugString ("Failed to open the X display.");
if (juce_isRunningAsApplication())
Process::terminate();
return;
}
// Get defaults for various properties
int screen = DefaultScreen (display);
Window root = RootWindow (display, screen);
Visual* visual = DefaultVisual (display, screen);
// Create atoms for our ClientMessages (these cannot be deleted)
specialId = XInternAtom (display, SpecialAtom, false);
broadcastId = XInternAtom (display, BroadcastAtom, false);
specialCallbackId = XInternAtom (display, SpecialCallbackAtom, false);
// 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()
{
if (errorCondition == false)
{
XDestroyWindow (display, juce_messageWindowHandle);
XCloseDisplay (display);
// reset pointers
juce_messageWindowHandle = 0;
display = 0;
// Restore original error handlers
XSetIOErrorHandler (oldIOErrorHandler);
XSetErrorHandler (oldErrorHandler);
}
}
bool juce_postMessageToSystemQueue (void* message)
{
if (errorCondition)
return false;
XClientMessageEvent clientMsg;
clientMsg.display = display;
clientMsg.window = juce_messageWindowHandle;
clientMsg.type = ClientMessage;
clientMsg.format = 32;
clientMsg.message_type = specialId;
#if JUCE_64BIT
clientMsg.data.l[0] = (long) (0x00000000ffffffff & (((uint64) message) >> 32));
clientMsg.data.l[1] = (long) (0x00000000ffffffff & (long) message);
#else
clientMsg.data.l[0] = (long) message;
#endif
XSendEvent (display, juce_messageWindowHandle, false,
NoEventMask, (XEvent*) &clientMsg);
XFlush (display); // This is necessary to ensure the event is delivered
return true;
}
void MessageManager::broadcastMessage (const String& value) throw()
{
}
void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func,
void* parameter)
{
void* retVal = 0;
if (! errorCondition)
{
if (! isThisTheMessageThread())
{
static MessageThreadFuncCall messageFuncCallContext;
const ScopedLock sl (messageFuncCallContext.lock);
XClientMessageEvent clientMsg;
clientMsg.display = display;
clientMsg.window = juce_messageWindowHandle;
clientMsg.type = ClientMessage;
clientMsg.format = 32;
clientMsg.message_type = specialCallbackId;
#if JUCE_64BIT
clientMsg.data.l[0] = (long) (0x00000000ffffffff & (((uint64) &messageFuncCallContext) >> 32));
clientMsg.data.l[1] = (long) (0x00000000ffffffff & (uint64) &messageFuncCallContext);
#else
clientMsg.data.l[0] = (long) &messageFuncCallContext;
#endif
messageFuncCallContext.func = func;
messageFuncCallContext.parameter = parameter;
if (XSendEvent (display, juce_messageWindowHandle, false, NoEventMask, (XEvent*) &clientMsg) == 0)
return 0;
XFlush (display); // This is necessary to ensure the event is delivered
// Wait for it to complete before continuing
messageFuncCallContext.event.wait();
retVal = messageFuncCallContext.result;
}
else
{
// Just call the function directly
retVal = func (parameter);
}
}
return retVal;
}
bool juce_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
{
if (errorCondition)
return false;
if (breakIn)
{
errorCondition = true;
if (juce_isRunningAsApplication())
Process::terminate();
return false;
}
if (returnIfNoPendingMessages && ! XPending (display))
return false;
XEvent evt;
XNextEvent (display, &evt);
if (evt.type == ClientMessage && evt.xany.window == juce_messageWindowHandle)
{
XClientMessageEvent* const clientMsg = (XClientMessageEvent*) &evt;
if (clientMsg->format != 32)
{
jassertfalse
DBG ("Error: juce_dispatchNextMessageOnSystemQueue received malformed client message.");
}
else
{
JUCE_TRY
{
#if JUCE_64BIT
void* const messagePtr
= (void*) ((0xffffffff00000000 & (((uint64) clientMsg->data.l[0]) << 32))
| (clientMsg->data.l[1] & 0x00000000ffffffff));
#else
void* const messagePtr = (void*) (clientMsg->data.l[0]);
#endif
if (clientMsg->message_type == specialId)
{
MessageManager::getInstance()->deliverMessage (messagePtr);
}
else if (clientMsg->message_type == specialCallbackId)
{
MessageThreadFuncCall* const call = (MessageThreadFuncCall*) messagePtr;
MessageCallbackFunction* func = call->func;
call->result = (*func) (call->parameter);
call->event.signal();
}
else if (clientMsg->message_type == broadcastId)
{
#if 0
TCHAR buffer[8192];
zeromem (buffer, sizeof (buffer));
if (GlobalGetAtomName ((ATOM) lParam, buffer, 8192) != 0)
mq->deliverBroadcastMessage (String (buffer));
#endif
}
else
{
DBG ("Error: juce_dispatchNextMessageOnSystemQueue received unknown client message.");
}
}
JUCE_CATCH_ALL
}
}
else if (evt.xany.window != juce_messageWindowHandle)
{
juce_windowMessageReceive (&evt);
}
return true;
}
END_JUCE_NAMESPACE
#endif