blob: 4237944230b10f083f68dd823fbc14b4549a583d [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"
#include <string.h>
#include <unistd.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <stdio.h>
#include <osl/process.h>
#include <osl/security.h>
#include <osl/conditn.h>
#include <tools/prex.h>
#include <X11/Xatom.h>
#include <tools/postx.h>
#include <unx/sm.hxx>
#include <unx/saldata.hxx>
#include <unx/saldisp.hxx>
#include <unx/salframe.h>
#include <unx/salinst.h>
#include <vcl/svapp.hxx>
#include <vcl/window.hxx>
#define USE_SM_EXTENSION
#if OSL_DEBUG_LEVEL > 1
#include <cstdarg>
static bool bFirstAssert = true;
#endif
#if OSL_DEBUG_LEVEL > 1
inline void SMprintf( const char* pFormat, ... )
#else
inline void SMprintf( const char*, ... )
#endif
{
#if OSL_DEBUG_LEVEL > 1
FILE* fp = fopen( "/tmp/sessionlog.txt", bFirstAssert ? "w" : "a" );
if(!fp) return;
bFirstAssert = false;
std::va_list ap;
va_start( ap, pFormat );
vfprintf( fp, pFormat, ap );
fclose( fp );
va_end( ap );
#endif
};
static IceSalSession* pOneInstance = NULL;
SalSession* X11SalInstance::CreateSalSession()
{
if( ! pOneInstance )
pOneInstance = new IceSalSession();
return pOneInstance;
}
/*
* class IceSalSession
*/
static X11SalFrame* pOldStyleSaveFrame = NULL;
IceSalSession::IceSalSession()
{
}
IceSalSession::~IceSalSession()
{
if( pOneInstance == this )
pOneInstance = NULL;
}
void IceSalSession::queryInteraction()
{
if( ! SessionManagerClient::queryInteraction() )
{
SalSessionInteractionEvent aEvent( false );
CallCallback( &aEvent );
}
}
void IceSalSession::interactionDone()
{
SessionManagerClient::interactionDone( false );
}
void IceSalSession::saveDone()
{
SessionManagerClient::saveDone();
if( pOldStyleSaveFrame )
{
// note: does nothing if not running in generic plugin
X11SalFrame::SaveYourselfDone( pOldStyleSaveFrame );
}
}
bool IceSalSession::cancelShutdown()
{
SessionManagerClient::interactionDone( true );
return false;
}
void IceSalSession::handleOldX11SaveYourself( SalFrame* pFrame )
{
// do this only once
if( ! pOldStyleSaveFrame )
{
pOldStyleSaveFrame = static_cast<X11SalFrame*>(pFrame);
if( pOneInstance )
{
SalSessionSaveRequestEvent aEvent( true, false );
pOneInstance->CallCallback( &aEvent );
}
}
}
extern "C" void SAL_CALL ICEConnectionWorker( void* );
class ICEConnectionObserver
{
friend void SAL_CALL ICEConnectionWorker(void*);
static sal_Bool bIsWatching;
static void ICEWatchProc( IceConn connection, IcePointer client_data,
Bool opening, IcePointer* watch_data );
static struct pollfd* pFilehandles;
static IceConn* pConnections;
static int nConnections;
static int nWakeupFiles[2];
static oslMutex ICEMutex;
static oslThread ICEThread;
#ifdef USE_SM_EXTENSION
static IceIOErrorHandler origIOErrorHandler;
static IceErrorHandler origErrorHandler;
#endif
public:
static void activate();
static void deactivate();
static void lock();
static void unlock();
static void wakeup();
};
SmcConn SessionManagerClient::aSmcConnection = NULL;
ByteString SessionManagerClient::aClientID;
sal_Bool ICEConnectionObserver::bIsWatching = sal_False;
struct pollfd* ICEConnectionObserver::pFilehandles = NULL;
IceConn* ICEConnectionObserver::pConnections = NULL;
int ICEConnectionObserver::nConnections = 0;
oslMutex ICEConnectionObserver::ICEMutex = NULL;
oslThread ICEConnectionObserver::ICEThread = NULL;
int ICEConnectionObserver::nWakeupFiles[2] = { 0, 0 };
#ifdef USE_SM_EXTENSION
IceIOErrorHandler ICEConnectionObserver::origIOErrorHandler = NULL;
IceErrorHandler ICEConnectionObserver::origErrorHandler = NULL;
static void IgnoreIceErrors(IceConn, Bool, int, unsigned long, int, int, IcePointer)
{
}
static void IgnoreIceIOErrors(IceConn)
{
}
#endif
// HACK
bool SessionManagerClient::bDocSaveDone = false;
static SmProp* pSmProps = NULL;
static SmProp** ppSmProps = NULL;
static int nSmProps = 0;
static unsigned char *pSmRestartHint = NULL;
static void BuildSmPropertyList()
{
if( ! pSmProps )
{
ByteString aExec( SessionManagerClient::getExecName(), osl_getThreadTextEncoding() );
nSmProps = 5;
pSmProps = new SmProp[ nSmProps ];
pSmProps[ 0 ].name = const_cast<char*>(SmCloneCommand);
pSmProps[ 0 ].type = const_cast<char*>(SmLISTofARRAY8);
pSmProps[ 0 ].num_vals = 1;
pSmProps[ 0 ].vals = new SmPropValue;
pSmProps[ 0 ].vals->length = aExec.Len()+1;
pSmProps[ 0 ].vals->value = strdup( aExec.GetBuffer() );
pSmProps[ 1 ].name = const_cast<char*>(SmProgram);
pSmProps[ 1 ].type = const_cast<char*>(SmARRAY8);
pSmProps[ 1 ].num_vals = 1;
pSmProps[ 1 ].vals = new SmPropValue;
pSmProps[ 1 ].vals->length = aExec.Len()+1;
pSmProps[ 1 ].vals->value = strdup( aExec.GetBuffer() );
pSmProps[ 2 ].name = const_cast<char*>(SmRestartCommand);
pSmProps[ 2 ].type = const_cast<char*>(SmLISTofARRAY8);
pSmProps[ 2 ].num_vals = 3;
pSmProps[ 2 ].vals = new SmPropValue[3];
pSmProps[ 2 ].vals[0].length = aExec.Len()+1;
pSmProps[ 2 ].vals[0].value = strdup( aExec.GetBuffer() );
ByteString aRestartOption( "-session=" );
aRestartOption.Append( SessionManagerClient::getSessionID() );
pSmProps[ 2 ].vals[1].length = aRestartOption.Len()+1;
pSmProps[ 2 ].vals[1].value = strdup( aRestartOption.GetBuffer() );
ByteString aRestartOptionNoLogo( "-nologo" );
pSmProps[ 2 ].vals[2].length = aRestartOptionNoLogo.Len()+1;
pSmProps[ 2 ].vals[2].value = strdup( aRestartOptionNoLogo.GetBuffer() );
rtl::OUString aUserName;
rtl::OString aUser;
oslSecurity aSec = osl_getCurrentSecurity();
if( aSec )
{
osl_getUserName( aSec, &aUserName.pData );
aUser = rtl::OUStringToOString( aUserName, osl_getThreadTextEncoding() );
osl_freeSecurityHandle( aSec );
}
pSmProps[ 3 ].name = const_cast<char*>(SmUserID);
pSmProps[ 3 ].type = const_cast<char*>(SmARRAY8);
pSmProps[ 3 ].num_vals = 1;
pSmProps[ 3 ].vals = new SmPropValue;
pSmProps[ 3 ].vals->value = strdup( aUser.getStr() );
pSmProps[ 3 ].vals->length = strlen( (char *)pSmProps[ 3 ].vals->value )+1;
pSmProps[ 4 ].name = const_cast<char*>(SmRestartStyleHint);
pSmProps[ 4 ].type = const_cast<char*>(SmCARD8);
pSmProps[ 4 ].num_vals = 1;
pSmProps[ 4 ].vals = new SmPropValue;
pSmProps[ 4 ].vals->value = malloc(1);
pSmRestartHint = (unsigned char *)pSmProps[ 4 ].vals->value;
*pSmRestartHint = SmRestartIfRunning;
pSmProps[ 4 ].vals->length = 1;
ppSmProps = new SmProp*[ nSmProps ];
for( int i = 0; i < nSmProps; i++ )
ppSmProps[ i ] = &pSmProps[i];
}
}
bool SessionManagerClient::checkDocumentsSaved()
{
return bDocSaveDone;
}
IMPL_STATIC_LINK( SessionManagerClient, SaveYourselfHdl, void*, EMPTYARG )
{
SMprintf( "posting save documents event shutdown = %s\n", (pThis!=0) ? "true" : "false" );
static bool bFirstShutdown=true;
if (pThis != 0 && bFirstShutdown) //first shutdown request
{
bFirstShutdown = false;
/*
If we have no actual frames open, e.g. we launched a quickstarter,
and then shutdown all our frames leaving just a quickstarter running,
then we don't want to launch an empty toplevel frame on the next
start. (The job of scheduling the restart of the quick-starter is a
task of the quick-starter)
*/
*pSmRestartHint = SmRestartNever;
const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames();
for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it )
{
Window *pWindow = (*it)->GetWindow();
if (pWindow && pWindow->IsVisible())
{
*pSmRestartHint = SmRestartIfRunning;
break;
}
}
}
if( pOneInstance )
{
SalSessionSaveRequestEvent aEvent( pThis != 0, false );
pOneInstance->CallCallback( &aEvent );
}
else
saveDone();
return 0;
}
IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, InteractionHdl, void*, EMPTYARG )
{
SMprintf( "interaction link\n" );
if( pOneInstance )
{
SalSessionInteractionEvent aEvent( true );
pOneInstance->CallCallback( &aEvent );
}
return 0;
}
IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, ShutDownCancelHdl, void*, EMPTYARG )
{
SMprintf( "shutdown cancel\n" );
if( pOneInstance )
{
SalSessionShutdownCancelEvent aEvent;
pOneInstance->CallCallback( &aEvent );
}
return 0;
}
void SessionManagerClient::SaveYourselfProc(
SmcConn,
SmPointer,
int save_type,
Bool shutdown,
int interact_style,
Bool
)
{
SMprintf( "Session: save yourself, save_type = %s, shutdown = %s, interact_style = %s, fast = %s\n",
save_type == SmSaveLocal ? "SmcSaveLocal" :
( save_type == SmSaveGlobal ? "SmcSaveGlobal" :
( save_type == SmSaveBoth ? "SmcSaveBoth" : "<unknown>" ) ),
shutdown ? "true" : "false",
interact_style == SmInteractStyleNone ? "SmInteractStyleNone" :
( interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
( interact_style == SmInteractStyleAny ? "SmInteractStyleAny" : "<unknown>" ) ),
false ? "true" : "false"
);
BuildSmPropertyList();
#ifdef USE_SM_EXTENSION
bDocSaveDone = false;
/* #i49875# some session managers send a "die" message if the
* saveDone does not come early enough for their convenience
* this can occasionally happen on startup, especially the first
* startup. So shortcut the "not shutting down" case since the
* upper layers are currently not interested in that event anyway.
*/
if( ! shutdown )
{
SessionManagerClient::saveDone();
return;
}
Application::PostUserEvent( STATIC_LINK( (void*)(shutdown ? 0xffffffff : 0x0), SessionManagerClient, SaveYourselfHdl ) );
SMprintf( "waiting for save yourself event to be processed\n" );
#endif
}
IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, ShutDownHdl, void*, EMPTYARG )
{
if( pOneInstance )
{
SalSessionQuitEvent aEvent;
pOneInstance->CallCallback( &aEvent );
}
const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames();
SMprintf( rFrames.begin() != rFrames.end() ? "shutdown on first frame\n" : "shutdown event but no frame\n" );
if( rFrames.begin() != rFrames.end() )
rFrames.front()->CallCallback( SALEVENT_SHUTDOWN, 0 );
return 0;
}
void SessionManagerClient::DieProc(
SmcConn connection,
SmPointer
)
{
SMprintf( "Session: die\n" );
if( connection == aSmcConnection )
{
Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, ShutDownHdl ) );
SMprintf( "waiting for shutdown event to be processed\n" );
}
}
void SessionManagerClient::SaveCompleteProc(
SmcConn,
SmPointer
)
{
SMprintf( "Session: save complete\n" );
}
void SessionManagerClient::ShutdownCanceledProc(
SmcConn connection,
SmPointer )
{
SMprintf( "Session: shutdown canceled\n" );
if( connection == aSmcConnection )
Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, ShutDownCancelHdl ) );
}
void SessionManagerClient::InteractProc(
SmcConn connection,
SmPointer )
{
SMprintf( "Session: interaction request completed\n" );
if( connection == aSmcConnection )
Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, InteractionHdl ) );
}
void SessionManagerClient::saveDone()
{
if( aSmcConnection )
{
ICEConnectionObserver::lock();
SmcSetProperties( aSmcConnection, nSmProps, ppSmProps );
SmcSaveYourselfDone( aSmcConnection, True );
SMprintf( "sent SaveYourselfDone SmRestartHint of %d\n", *pSmRestartHint );
bDocSaveDone = true;
ICEConnectionObserver::unlock();
}
}
void SessionManagerClient::open()
{
static SmcCallbacks aCallbacks;
#ifdef USE_SM_EXTENSION
// this is the way Xt does it, so we can too
if( ! aSmcConnection && getenv( "SESSION_MANAGER" ) )
{
char aErrBuf[1024];
ICEConnectionObserver::activate();
ICEConnectionObserver::lock();
char* pClientID = NULL;
const ByteString& rPrevId( getPreviousSessionID() );
aCallbacks.save_yourself.callback = SaveYourselfProc;
aCallbacks.save_yourself.client_data = NULL;
aCallbacks.die.callback = DieProc;
aCallbacks.die.client_data = NULL;
aCallbacks.save_complete.callback = SaveCompleteProc;
aCallbacks.save_complete.client_data = NULL;
aCallbacks.shutdown_cancelled.callback = ShutdownCanceledProc;
aCallbacks.shutdown_cancelled.client_data = NULL;
aSmcConnection = SmcOpenConnection( NULL,
NULL,
SmProtoMajor,
SmProtoMinor,
SmcSaveYourselfProcMask |
SmcDieProcMask |
SmcSaveCompleteProcMask |
SmcShutdownCancelledProcMask ,
&aCallbacks,
rPrevId.Len() ? const_cast<char*>(rPrevId.GetBuffer()) : NULL,
&pClientID,
sizeof( aErrBuf ),
aErrBuf );
if( ! aSmcConnection )
SMprintf( "SmcOpenConnection failed: %s\n", aErrBuf );
else
SMprintf( "SmcOpenConnection succeeded, client ID is \"%s\"\n", pClientID );
aClientID = ByteString( pClientID );
free( pClientID );
pClientID = NULL;
ICEConnectionObserver::unlock();
SalDisplay* pDisp = GetX11SalData()->GetDisplay();
if( pDisp->GetDrawable( pDisp->GetDefaultScreenNumber() ) && aClientID.Len() )
{
XChangeProperty( pDisp->GetDisplay(),
pDisp->GetDrawable( pDisp->GetDefaultScreenNumber() ),
XInternAtom( pDisp->GetDisplay(), "SM_CLIENT_ID", False ),
XA_STRING,
8,
PropModeReplace,
(unsigned char*)aClientID.GetBuffer(),
aClientID.Len()
);
}
}
else if( ! aSmcConnection )
SMprintf( "no SESSION_MANAGER\n" );
#endif
}
const ByteString& SessionManagerClient::getSessionID()
{
return aClientID;
}
void SessionManagerClient::close()
{
if( aSmcConnection )
{
#ifdef USE_SM_EXTENSION
ICEConnectionObserver::lock();
SMprintf( "attempting SmcCloseConnection\n" );
SmcCloseConnection( aSmcConnection, 0, NULL );
SMprintf( "SmcConnection closed\n" );
ICEConnectionObserver::unlock();
ICEConnectionObserver::deactivate();
#endif
aSmcConnection = NULL;
}
}
bool SessionManagerClient::queryInteraction()
{
bool bRet = false;
if( aSmcConnection )
{
ICEConnectionObserver::lock();
if( SmcInteractRequest( aSmcConnection, SmDialogNormal, InteractProc, NULL ) )
bRet = true;
ICEConnectionObserver::unlock();
}
return bRet;
}
void SessionManagerClient::interactionDone( bool bCancelShutdown )
{
if( aSmcConnection )
{
ICEConnectionObserver::lock();
SmcInteractDone( aSmcConnection, bCancelShutdown ? True : False );
ICEConnectionObserver::unlock();
}
}
String SessionManagerClient::getExecName()
{
rtl::OUString aExec, aSysExec;
osl_getExecutableFile( &aExec.pData );
osl_getSystemPathFromFileURL( aExec.pData, &aSysExec.pData );
int nPos = aSysExec.indexOf( rtl::OUString::createFromAscii( ".bin" ) );
if( nPos != -1 )
aSysExec = aSysExec.copy( 0, nPos );
return aSysExec;
}
const ByteString& SessionManagerClient::getPreviousSessionID()
{
static ByteString aPrevId;
int nCommands = osl_getCommandArgCount();
for( int i = 0; i < nCommands; i++ )
{
::rtl::OUString aArg;
osl_getCommandArg( i, &aArg.pData );
if( aArg.compareToAscii( "-session=", 9 ) == 0 )
{
aPrevId = ByteString( ::rtl::OUStringToOString( aArg.copy( 9 ), osl_getThreadTextEncoding() ) );
break;
}
}
SMprintf( "previous ID = \"%s\"\n", aPrevId.GetBuffer() );
return aPrevId;
}
void ICEConnectionObserver::lock()
{
osl_acquireMutex( ICEMutex );
}
void ICEConnectionObserver::unlock()
{
osl_releaseMutex( ICEMutex );
}
void ICEConnectionObserver::activate()
{
if( ! bIsWatching )
{
nWakeupFiles[0] = nWakeupFiles[1] = 0;
ICEMutex = osl_createMutex();
bIsWatching = sal_True;
#ifdef USE_SM_EXTENSION
/*
* Default handlers call exit, we don't care that strongly if something
* happens to fail
*/
origIOErrorHandler = IceSetIOErrorHandler( IgnoreIceIOErrors );
origErrorHandler = IceSetErrorHandler( IgnoreIceErrors );
IceAddConnectionWatch( ICEWatchProc, NULL );
#endif
}
}
void ICEConnectionObserver::deactivate()
{
if( bIsWatching )
{
lock();
bIsWatching = sal_False;
#ifdef USE_SM_EXTENSION
IceRemoveConnectionWatch( ICEWatchProc, NULL );
IceSetErrorHandler( origErrorHandler );
IceSetIOErrorHandler( origIOErrorHandler );
#endif
nConnections = 0;
if( ICEThread )
{
osl_terminateThread( ICEThread );
wakeup();
}
unlock();
if( ICEThread )
{
osl_joinWithThread( ICEThread );
osl_destroyThread( ICEThread );
close( nWakeupFiles[1] );
close( nWakeupFiles[0] );
ICEThread = NULL;
}
osl_destroyMutex( ICEMutex );
ICEMutex = NULL;
}
}
void ICEConnectionObserver::wakeup()
{
char cChar = 'w';
write( nWakeupFiles[1], &cChar, 1 );
}
void ICEConnectionWorker( void* )
{
#ifdef USE_SM_EXTENSION
while( osl_scheduleThread(ICEConnectionObserver::ICEThread) && ICEConnectionObserver::nConnections )
{
ICEConnectionObserver::lock();
int nConnectionsBefore = ICEConnectionObserver::nConnections;
int nBytes = sizeof( struct pollfd )*(nConnectionsBefore+1);
struct pollfd* pLocalFD = (struct pollfd*)rtl_allocateMemory( nBytes );
rtl_copyMemory( pLocalFD, ICEConnectionObserver::pFilehandles, nBytes );
ICEConnectionObserver::unlock();
int nRet = poll( pLocalFD,nConnectionsBefore+1,-1 );
bool bWakeup = (pLocalFD[0].revents & POLLIN);
rtl_freeMemory( pLocalFD );
if( nRet < 1 )
continue;
// clear wakeup pipe
if( bWakeup )
{
char buf[4];
while( read( ICEConnectionObserver::nWakeupFiles[0], buf, sizeof( buf ) ) > 0 )
;
SMprintf( "file handles active in wakeup: %d\n", nRet );
if( nRet == 1 )
continue;
}
// check fd's after we obtained the lock
ICEConnectionObserver::lock();
if( ICEConnectionObserver::nConnections > 0 && ICEConnectionObserver::nConnections == nConnectionsBefore )
{
nRet = poll( ICEConnectionObserver::pFilehandles+1, ICEConnectionObserver::nConnections, 0 );
if( nRet > 0 )
{
SMprintf( "IceProcessMessages\n" );
Bool bReply;
for( int i = 0; i < ICEConnectionObserver::nConnections; i++ )
if( ICEConnectionObserver::pFilehandles[i+1].revents & POLLIN )
IceProcessMessages( ICEConnectionObserver::pConnections[i], NULL, &bReply );
}
}
ICEConnectionObserver::unlock();
}
#endif
SMprintf( "shutting donw ICE dispatch thread\n" );
}
void ICEConnectionObserver::ICEWatchProc(
IceConn connection,
IcePointer,
Bool opening,
IcePointer*
)
{
// note: this is a callback function for ICE
// this implicitly means that a call into ICE lib is calling this
// so the ICEMutex MUST already be locked by the caller
#ifdef USE_SM_EXTENSION
if( opening )
{
int fd = IceConnectionNumber( connection );
nConnections++;
pConnections = (IceConn*)rtl_reallocateMemory( pConnections, sizeof( IceConn )*nConnections );
pFilehandles = (struct pollfd*)rtl_reallocateMemory( pFilehandles, sizeof( struct pollfd )*(nConnections+1) );
pConnections[ nConnections-1 ] = connection;
pFilehandles[ nConnections ].fd = fd;
pFilehandles[ nConnections ].events = POLLIN;
if( nConnections == 1 )
{
if( ! pipe( nWakeupFiles ) )
{
int flags;
pFilehandles[0].fd = nWakeupFiles[0];
pFilehandles[0].events = POLLIN;
// set close-on-exec and nonblock descriptor flag.
if ((flags = fcntl (nWakeupFiles[0], F_GETFD)) != -1)
{
flags |= FD_CLOEXEC;
fcntl (nWakeupFiles[0], F_SETFD, flags);
}
if ((flags = fcntl (nWakeupFiles[0], F_GETFL)) != -1)
{
flags |= O_NONBLOCK;
fcntl (nWakeupFiles[0], F_SETFL, flags);
}
// set close-on-exec and nonblock descriptor flag.
if ((flags = fcntl (nWakeupFiles[1], F_GETFD)) != -1)
{
flags |= FD_CLOEXEC;
fcntl (nWakeupFiles[1], F_SETFD, flags);
}
if ((flags = fcntl (nWakeupFiles[1], F_GETFL)) != -1)
{
flags |= O_NONBLOCK;
fcntl (nWakeupFiles[1], F_SETFL, flags);
}
ICEThread = osl_createSuspendedThread( ICEConnectionWorker, NULL );
osl_resumeThread( ICEThread );
}
}
}
else
{
for( int i = 0; i < nConnections; i++ )
{
if( pConnections[i] == connection )
{
if( i < nConnections-1 )
{
rtl_moveMemory( pConnections+i, pConnections+i+1, sizeof( IceConn )*(nConnections-i-1) );
rtl_moveMemory( pFilehandles+i+1, pFilehandles+i+2, sizeof( struct pollfd )*(nConnections-i-1) );
}
nConnections--;
pConnections = (IceConn*)rtl_reallocateMemory( pConnections, sizeof( IceConn )*nConnections );
pFilehandles = (struct pollfd*)rtl_reallocateMemory( pFilehandles, sizeof( struct pollfd )*(nConnections+1) );
break;
}
}
if( nConnections == 0 && ICEThread )
{
SMprintf( "terminating ICEThread\n" );
osl_terminateThread( ICEThread );
wakeup();
// must release the mutex here
osl_releaseMutex( ICEMutex );
osl_joinWithThread( ICEThread );
osl_destroyThread( ICEThread );
close( nWakeupFiles[1] );
close( nWakeupFiles[0] );
ICEThread = NULL;
}
}
SMprintf( "ICE connection on %d %s\n",
IceConnectionNumber( connection ),
opening ? "inserted" : "removed" );
SMprintf( "Display connection is %d\n", ConnectionNumber( GetX11SalData()->GetDisplay()->GetDisplay() ) );
#endif
}