/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
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 ) ;
( void ) 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 ) ;
( void ) 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 ) ;
( void ) 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 )
{
( void ) display ; ( void ) 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 ) ;
XCloseDisplay ( display ) ;
juce_messageWindowHandle = 0 ;
display = nullptr ;
LinuxErrorHandling : : removeXErrorHandlers ( ) ;
}
}
bool MessageManager : : postMessageToSystemQueue ( MessageManager : : MessageBase * const message )
{
if ( LinuxErrorHandling : : errorOccurred )
return false ;
InternalMessageQueue : : getInstanceWithoutCreating ( ) - > postMessage ( message ) ;
return true ;
}
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 ;
}
InternalMessageQueue * const queue = InternalMessageQueue : : getInstanceWithoutCreating ( ) ;
jassert ( queue ! = nullptr ) ;
if ( queue - > dispatchNextEvent ( ) )
return true ;
if ( returnIfNoPendingMessages )
break ;
queue - > sleepUntilEvent ( 2000 ) ;
}
return false ;
}