xref: /trunk/main/vcl/unx/generic/app/sm.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/poll.h>
33 #include <fcntl.h>
34 
35 #include <stdio.h>
36 
37 #include <osl/process.h>
38 #include <osl/security.h>
39 #include <osl/conditn.h>
40 
41 #include <tools/prex.h>
42 #include <X11/Xatom.h>
43 #include <tools/postx.h>
44 
45 #include <unx/sm.hxx>
46 #include <unx/saldata.hxx>
47 #include <unx/saldisp.hxx>
48 #include <unx/salframe.h>
49 #include <unx/salinst.h>
50 
51 #include <vcl/svapp.hxx>
52 #include <vcl/window.hxx>
53 
54 #define USE_SM_EXTENSION
55 
56 #if OSL_DEBUG_LEVEL > 1
57 #include <cstdarg>
58 static bool bFirstAssert = true;
59 #endif
60 
61 #if OSL_DEBUG_LEVEL > 1
62 inline void SMprintf( const char* pFormat, ... )
63 #else
64 inline void SMprintf( const char*, ... )
65 #endif
66 {
67 #if OSL_DEBUG_LEVEL > 1
68     FILE* fp = fopen( "/tmp/sessionlog.txt", bFirstAssert ? "w" : "a" );
69     if(!fp) return;
70     bFirstAssert = false;
71     std::va_list ap;
72     va_start( ap, pFormat );
73     vfprintf( fp, pFormat, ap );
74     fclose( fp );
75     va_end( ap );
76 #endif
77 };
78 
79 static IceSalSession* pOneInstance = NULL;
80 
81 SalSession* X11SalInstance::CreateSalSession()
82 {
83     if( ! pOneInstance )
84         pOneInstance = new IceSalSession();
85     return pOneInstance;
86 }
87 
88 /*
89  *  class IceSalSession
90  */
91 
92 static X11SalFrame* pOldStyleSaveFrame = NULL;
93 
94 IceSalSession::IceSalSession()
95 {
96 }
97 
98 IceSalSession::~IceSalSession()
99 {
100     if( pOneInstance == this )
101         pOneInstance = NULL;
102 }
103 
104 void IceSalSession::queryInteraction()
105 {
106     if( ! SessionManagerClient::queryInteraction() )
107     {
108         SalSessionInteractionEvent aEvent( false );
109         CallCallback( &aEvent );
110     }
111 }
112 
113 void IceSalSession::interactionDone()
114 {
115     SessionManagerClient::interactionDone( false );
116 }
117 
118 void IceSalSession::saveDone()
119 {
120     SessionManagerClient::saveDone();
121     if( pOldStyleSaveFrame )
122     {
123         // note: does nothing if not running in generic plugin
124         X11SalFrame::SaveYourselfDone( pOldStyleSaveFrame );
125     }
126 }
127 
128 bool IceSalSession::cancelShutdown()
129 {
130     SessionManagerClient::interactionDone( true );
131     return false;
132 }
133 
134 void IceSalSession::handleOldX11SaveYourself( SalFrame* pFrame )
135 {
136     // do this only once
137     if( ! pOldStyleSaveFrame )
138     {
139         pOldStyleSaveFrame = static_cast<X11SalFrame*>(pFrame);
140         if( pOneInstance )
141         {
142             SalSessionSaveRequestEvent aEvent( true, false );
143             pOneInstance->CallCallback( &aEvent );
144         }
145     }
146 }
147 
148 extern "C" void SAL_CALL ICEConnectionWorker( void* );
149 
150 class ICEConnectionObserver
151 {
152     friend void SAL_CALL ICEConnectionWorker(void*);
153 	static sal_Bool bIsWatching;
154 	static void ICEWatchProc( IceConn connection, IcePointer client_data,
155 							  Bool opening, IcePointer* watch_data );
156 
157     static struct pollfd* pFilehandles;
158     static IceConn* pConnections;
159     static int nConnections;
160     static int nWakeupFiles[2];
161     static oslMutex ICEMutex;
162     static oslThread ICEThread;
163 #ifdef USE_SM_EXTENSION
164     static IceIOErrorHandler origIOErrorHandler;
165     static IceErrorHandler origErrorHandler;
166 #endif
167 public:
168 
169 	static void activate();
170     static void deactivate();
171     static void lock();
172     static void unlock();
173     static void wakeup();
174 };
175 
176 
177 SmcConn				SessionManagerClient::aSmcConnection			= NULL;
178 ByteString			SessionManagerClient::aClientID;
179 sal_Bool				ICEConnectionObserver::bIsWatching				= sal_False;
180 struct pollfd* 	ICEConnectionObserver::pFilehandles				= NULL;
181 IceConn*			ICEConnectionObserver::pConnections				= NULL;
182 int					ICEConnectionObserver::nConnections				= 0;
183 oslMutex			ICEConnectionObserver::ICEMutex					= NULL;
184 oslThread			ICEConnectionObserver::ICEThread				= NULL;
185 int					ICEConnectionObserver::nWakeupFiles[2]			= { 0, 0 };
186 
187 #ifdef USE_SM_EXTENSION
188 IceIOErrorHandler ICEConnectionObserver::origIOErrorHandler = NULL;
189 IceErrorHandler ICEConnectionObserver::origErrorHandler = NULL;
190 
191 static void IgnoreIceErrors(IceConn, Bool, int, unsigned long, int, int, IcePointer)
192 {
193 }
194 
195 static void IgnoreIceIOErrors(IceConn)
196 {
197 }
198 #endif
199 
200 // HACK
201 bool SessionManagerClient::bDocSaveDone = false;
202 
203 
204 static SmProp*	pSmProps = NULL;
205 static SmProp**	ppSmProps = NULL;
206 static int		nSmProps = 0;
207 static unsigned char   *pSmRestartHint = NULL;
208 
209 
210 static void BuildSmPropertyList()
211 {
212 	if( ! pSmProps )
213 	{
214 		ByteString aExec( SessionManagerClient::getExecName(), osl_getThreadTextEncoding() );
215 
216 		nSmProps = 5;
217 		pSmProps = new SmProp[ nSmProps ];
218 
219 		pSmProps[ 0 ].name		= const_cast<char*>(SmCloneCommand);
220 		pSmProps[ 0 ].type		= const_cast<char*>(SmLISTofARRAY8);
221 		pSmProps[ 0 ].num_vals	= 1;
222 		pSmProps[ 0 ].vals		= new SmPropValue;
223 		pSmProps[ 0 ].vals->length	= aExec.Len()+1;
224 		pSmProps[ 0 ].vals->value	= strdup( aExec.GetBuffer() );
225 
226 		pSmProps[ 1 ].name		= const_cast<char*>(SmProgram);
227 		pSmProps[ 1 ].type		= const_cast<char*>(SmARRAY8);
228 		pSmProps[ 1 ].num_vals	= 1;
229 		pSmProps[ 1 ].vals		= new SmPropValue;
230 		pSmProps[ 1 ].vals->length	= aExec.Len()+1;
231 		pSmProps[ 1 ].vals->value	= strdup( aExec.GetBuffer() );
232 
233 		pSmProps[ 2 ].name		= const_cast<char*>(SmRestartCommand);
234 		pSmProps[ 2 ].type		= const_cast<char*>(SmLISTofARRAY8);
235 		pSmProps[ 2 ].num_vals	= 3;
236 		pSmProps[ 2 ].vals		= new SmPropValue[3];
237 		pSmProps[ 2 ].vals[0].length	= aExec.Len()+1;
238 		pSmProps[ 2 ].vals[0].value	= strdup( aExec.GetBuffer() );
239         	ByteString aRestartOption( "-session=" );
240 		aRestartOption.Append( SessionManagerClient::getSessionID() );
241 		pSmProps[ 2 ].vals[1].length	= aRestartOption.Len()+1;
242 		pSmProps[ 2 ].vals[1].value	= strdup( aRestartOption.GetBuffer() );
243         	ByteString aRestartOptionNoLogo( "-nologo" );
244 		pSmProps[ 2 ].vals[2].length	= aRestartOptionNoLogo.Len()+1;
245 		pSmProps[ 2 ].vals[2].value	= strdup( aRestartOptionNoLogo.GetBuffer() );
246 
247 		rtl::OUString aUserName;
248         rtl::OString aUser;
249         oslSecurity aSec = osl_getCurrentSecurity();
250         if( aSec )
251         {
252             osl_getUserName( aSec, &aUserName.pData );
253             aUser = rtl::OUStringToOString( aUserName, osl_getThreadTextEncoding() );
254             osl_freeSecurityHandle( aSec );
255         }
256 
257 		pSmProps[ 3 ].name		= const_cast<char*>(SmUserID);
258 		pSmProps[ 3 ].type		= const_cast<char*>(SmARRAY8);
259 		pSmProps[ 3 ].num_vals	= 1;
260 		pSmProps[ 3 ].vals		= new SmPropValue;
261 		pSmProps[ 3 ].vals->value	= strdup( aUser.getStr() );
262 		pSmProps[ 3 ].vals->length	= strlen( (char *)pSmProps[ 3 ].vals->value )+1;
263 
264 		pSmProps[ 4 ].name		= const_cast<char*>(SmRestartStyleHint);
265 		pSmProps[ 4 ].type		= const_cast<char*>(SmCARD8);
266 		pSmProps[ 4 ].num_vals	= 1;
267 		pSmProps[ 4 ].vals		= new SmPropValue;
268 		pSmProps[ 4 ].vals->value	= malloc(1);
269 		pSmRestartHint = (unsigned char *)pSmProps[ 4 ].vals->value;
270 		*pSmRestartHint = SmRestartIfRunning;
271 		pSmProps[ 4 ].vals->length	= 1;
272 
273 		ppSmProps = new SmProp*[ nSmProps ];
274 		for( int i = 0; i < nSmProps; i++ )
275 			ppSmProps[ i ] = &pSmProps[i];
276 	}
277 }
278 
279 bool SessionManagerClient::checkDocumentsSaved()
280 {
281     return bDocSaveDone;
282 }
283 
284 IMPL_STATIC_LINK( SessionManagerClient, SaveYourselfHdl, void*, EMPTYARG )
285 {
286     SMprintf( "posting save documents event shutdown = %s\n", (pThis!=0) ? "true" : "false" );
287 
288     static bool bFirstShutdown=true;
289     if (pThis != 0 && bFirstShutdown) //first shutdown request
290     {
291         bFirstShutdown = false;
292         /*
293           If we have no actual frames open, e.g. we launched a quickstarter,
294           and then shutdown all our frames leaving just a quickstarter running,
295           then we don't want to launch an empty toplevel frame on the next
296           start. (The job of scheduling the restart of the quick-starter is a
297           task of the quick-starter)
298         */
299         *pSmRestartHint = SmRestartNever;
300         const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames();
301         for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it )
302         {
303             Window *pWindow = (*it)->GetWindow();
304             if (pWindow && pWindow->IsVisible())
305             {
306                 *pSmRestartHint = SmRestartIfRunning;
307                 break;
308             }
309         }
310     }
311 
312     if( pOneInstance )
313     {
314         SalSessionSaveRequestEvent aEvent( pThis != 0, false );
315         pOneInstance->CallCallback( &aEvent );
316     }
317     else
318         saveDone();
319 
320     return 0;
321 }
322 
323 IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, InteractionHdl, void*, EMPTYARG )
324 {
325     SMprintf( "interaction link\n" );
326     if( pOneInstance )
327     {
328         SalSessionInteractionEvent aEvent( true );
329         pOneInstance->CallCallback( &aEvent );
330     }
331 
332     return 0;
333 }
334 
335 IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, ShutDownCancelHdl, void*, EMPTYARG )
336 {
337     SMprintf( "shutdown cancel\n" );
338     if( pOneInstance )
339     {
340         SalSessionShutdownCancelEvent aEvent;
341         pOneInstance->CallCallback( &aEvent );
342     }
343 
344     return 0;
345 }
346 
347 void SessionManagerClient::SaveYourselfProc(
348 	SmcConn,
349 	SmPointer,
350 	int save_type,
351 	Bool shutdown,
352 	int interact_style,
353 	Bool
354 	)
355 {
356     SMprintf( "Session: save yourself, save_type = %s, shutdown = %s, interact_style = %s, fast = %s\n",
357               save_type == SmSaveLocal ? "SmcSaveLocal" :
358               ( save_type == SmSaveGlobal ? "SmcSaveGlobal" :
359                 ( save_type == SmSaveBoth ? "SmcSaveBoth" : "<unknown>" ) ),
360               shutdown ? "true" : "false",
361               interact_style == SmInteractStyleNone ? "SmInteractStyleNone" :
362               ( interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
363                 ( interact_style == SmInteractStyleAny ? "SmInteractStyleAny" : "<unknown>" ) ),
364               false ? "true" : "false"
365               );
366 	BuildSmPropertyList();
367 #ifdef USE_SM_EXTENSION
368     bDocSaveDone = false;
369     /* #i49875# some session managers send a "die" message if the
370      * saveDone does not come early enough for their convenience
371      * this can occasionally happen on startup, especially the first
372      * startup. So shortcut the "not shutting down" case since the
373      * upper layers are currently not interested in that event anyway.
374      */
375     if( ! shutdown )
376     {
377         SessionManagerClient::saveDone();
378         return;
379     }
380     Application::PostUserEvent( STATIC_LINK( (void*)(shutdown ? 0xffffffff : 0x0), SessionManagerClient, SaveYourselfHdl ) );
381     SMprintf( "waiting for save yourself event to be processed\n" );
382 #endif
383 }
384 
385 IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, ShutDownHdl, void*, EMPTYARG )
386 {
387     if( pOneInstance )
388     {
389         SalSessionQuitEvent aEvent;
390         pOneInstance->CallCallback( &aEvent );
391     }
392 
393     const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames();
394     SMprintf( rFrames.begin() != rFrames.end() ? "shutdown on first frame\n" : "shutdown event but no frame\n" );
395     if( rFrames.begin() != rFrames.end() )
396         rFrames.front()->CallCallback( SALEVENT_SHUTDOWN, 0 );
397     return 0;
398 }
399 
400 void SessionManagerClient::DieProc(
401 	SmcConn connection,
402 	SmPointer
403 	)
404 {
405     SMprintf( "Session: die\n" );
406 	if( connection == aSmcConnection )
407     {
408         Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, ShutDownHdl ) );
409         SMprintf( "waiting for shutdown event to be processed\n" );
410     }
411 }
412 
413 void SessionManagerClient::SaveCompleteProc(
414 	SmcConn,
415 	SmPointer
416 	)
417 {
418     SMprintf( "Session: save complete\n" );
419 }
420 
421 void SessionManagerClient::ShutdownCanceledProc(
422 	SmcConn connection,
423 	SmPointer )
424 {
425     SMprintf( "Session: shutdown canceled\n" );
426 	if( connection == aSmcConnection )
427         Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, ShutDownCancelHdl ) );
428 }
429 
430 void SessionManagerClient::InteractProc(
431                                         SmcConn connection,
432                                         SmPointer )
433 {
434     SMprintf( "Session: interaction request completed\n" );
435 	if( connection == aSmcConnection )
436         Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, InteractionHdl ) );
437 }
438 
439 void SessionManagerClient::saveDone()
440 {
441     if( aSmcConnection )
442     {
443         ICEConnectionObserver::lock();
444         SmcSetProperties( aSmcConnection, nSmProps, ppSmProps );
445         SmcSaveYourselfDone( aSmcConnection, True );
446         SMprintf( "sent SaveYourselfDone SmRestartHint of %d\n", *pSmRestartHint );
447         bDocSaveDone = true;
448         ICEConnectionObserver::unlock();
449     }
450 }
451 
452 
453 void SessionManagerClient::open()
454 {
455 	static SmcCallbacks aCallbacks;
456 
457 #ifdef USE_SM_EXTENSION
458 	// this is the way Xt does it, so we can too
459 	if( ! aSmcConnection && getenv( "SESSION_MANAGER" ) )
460 	{
461 		char aErrBuf[1024];
462 		ICEConnectionObserver::activate();
463         ICEConnectionObserver::lock();
464 
465         char* pClientID = NULL;
466         const ByteString& rPrevId( getPreviousSessionID() );
467 
468 		aCallbacks.save_yourself.callback			= SaveYourselfProc;
469 		aCallbacks.save_yourself.client_data		= NULL;
470 		aCallbacks.die.callback						= DieProc;
471 		aCallbacks.die.client_data					= NULL;
472 		aCallbacks.save_complete.callback			= SaveCompleteProc;
473 		aCallbacks.save_complete.client_data		= NULL;
474 		aCallbacks.shutdown_cancelled.callback		= ShutdownCanceledProc;
475 		aCallbacks.shutdown_cancelled.client_data	= NULL;
476 		aSmcConnection = SmcOpenConnection( NULL,
477 											NULL,
478 											SmProtoMajor,
479 											SmProtoMinor,
480 											SmcSaveYourselfProcMask			|
481 											SmcDieProcMask					|
482 											SmcSaveCompleteProcMask			|
483 											SmcShutdownCancelledProcMask	,
484 											&aCallbacks,
485 											rPrevId.Len() ? const_cast<char*>(rPrevId.GetBuffer()) : NULL,
486 											&pClientID,
487 											sizeof( aErrBuf ),
488 											aErrBuf );
489 		if( ! aSmcConnection )
490 			SMprintf( "SmcOpenConnection failed: %s\n", aErrBuf );
491         else
492             SMprintf( "SmcOpenConnection succeeded, client ID is \"%s\"\n", pClientID );
493         aClientID = ByteString( pClientID );
494         free( pClientID );
495         pClientID = NULL;
496         ICEConnectionObserver::unlock();
497 
498         SalDisplay* pDisp = GetX11SalData()->GetDisplay();
499         if( pDisp->GetDrawable( pDisp->GetDefaultScreenNumber() ) && aClientID.Len() )
500         {
501             XChangeProperty( pDisp->GetDisplay(),
502                              pDisp->GetDrawable( pDisp->GetDefaultScreenNumber() ),
503                              XInternAtom( pDisp->GetDisplay(), "SM_CLIENT_ID", False ),
504                              XA_STRING,
505                              8,
506                              PropModeReplace,
507                              (unsigned char*)aClientID.GetBuffer(),
508                              aClientID.Len()
509                              );
510         }
511 	}
512     else if( ! aSmcConnection )
513         SMprintf( "no SESSION_MANAGER\n" );
514 #endif
515 }
516 
517 const ByteString& SessionManagerClient::getSessionID()
518 {
519     return aClientID;
520 }
521 
522 void SessionManagerClient::close()
523 {
524 	if( aSmcConnection )
525 	{
526 #ifdef USE_SM_EXTENSION
527         ICEConnectionObserver::lock();
528         SMprintf( "attempting SmcCloseConnection\n" );
529 		SmcCloseConnection( aSmcConnection, 0, NULL );
530         SMprintf( "SmcConnection closed\n" );
531         ICEConnectionObserver::unlock();
532         ICEConnectionObserver::deactivate();
533 #endif
534 		aSmcConnection = NULL;
535 	}
536 }
537 
538 bool SessionManagerClient::queryInteraction()
539 {
540     bool bRet = false;
541     if( aSmcConnection )
542     {
543         ICEConnectionObserver::lock();
544         if( SmcInteractRequest( aSmcConnection, SmDialogNormal, InteractProc, NULL ) )
545             bRet = true;
546         ICEConnectionObserver::unlock();
547     }
548     return bRet;
549 }
550 
551 void SessionManagerClient::interactionDone( bool bCancelShutdown )
552 {
553     if( aSmcConnection )
554     {
555         ICEConnectionObserver::lock();
556         SmcInteractDone( aSmcConnection, bCancelShutdown ? True : False );
557         ICEConnectionObserver::unlock();
558     }
559 }
560 
561 
562 String SessionManagerClient::getExecName()
563 {
564 	rtl::OUString aExec, aSysExec;
565 	osl_getExecutableFile( &aExec.pData );
566     osl_getSystemPathFromFileURL( aExec.pData, &aSysExec.pData );
567 
568 	int nPos = aSysExec.indexOf( rtl::OUString::createFromAscii( ".bin" ) );
569 	if( nPos != -1 )
570 		aSysExec = aSysExec.copy( 0, nPos );
571 	return aSysExec;
572 }
573 
574 
575 const ByteString& SessionManagerClient::getPreviousSessionID()
576 {
577 	static ByteString aPrevId;
578 
579     int nCommands = osl_getCommandArgCount();
580     for( int i = 0; i < nCommands; i++ )
581     {
582         ::rtl::OUString aArg;
583         osl_getCommandArg( i, &aArg.pData );
584         if( aArg.compareToAscii( "-session=", 9 ) == 0 )
585         {
586             aPrevId = ByteString( ::rtl::OUStringToOString( aArg.copy( 9 ), osl_getThreadTextEncoding() ) );
587             break;
588         }
589     }
590     SMprintf( "previous ID = \"%s\"\n", aPrevId.GetBuffer() );
591     return aPrevId;
592 }
593 
594 void ICEConnectionObserver::lock()
595 {
596     osl_acquireMutex( ICEMutex );
597 }
598 
599 void ICEConnectionObserver::unlock()
600 {
601     osl_releaseMutex( ICEMutex );
602 }
603 
604 void ICEConnectionObserver::activate()
605 {
606     if( ! bIsWatching )
607     {
608         nWakeupFiles[0] = nWakeupFiles[1] = 0;
609         ICEMutex = osl_createMutex();
610         bIsWatching = sal_True;
611 #ifdef USE_SM_EXTENSION
612         /*
613          * Default handlers call exit, we don't care that strongly if something
614          * happens to fail
615          */
616         origIOErrorHandler = IceSetIOErrorHandler( IgnoreIceIOErrors );
617         origErrorHandler = IceSetErrorHandler( IgnoreIceErrors );
618         IceAddConnectionWatch( ICEWatchProc, NULL );
619 #endif
620     }
621 }
622 
623 void ICEConnectionObserver::deactivate()
624 {
625     if( bIsWatching )
626     {
627         lock();
628         bIsWatching = sal_False;
629 #ifdef USE_SM_EXTENSION
630         IceRemoveConnectionWatch( ICEWatchProc, NULL );
631         IceSetErrorHandler( origErrorHandler );
632         IceSetIOErrorHandler( origIOErrorHandler );
633 #endif
634         nConnections = 0;
635         if( ICEThread )
636         {
637             osl_terminateThread( ICEThread );
638             wakeup();
639         }
640         unlock();
641         if( ICEThread )
642         {
643             osl_joinWithThread( ICEThread );
644             osl_destroyThread( ICEThread );
645             close( nWakeupFiles[1] );
646             close( nWakeupFiles[0] );
647             ICEThread = NULL;
648         }
649         osl_destroyMutex( ICEMutex );
650         ICEMutex = NULL;
651     }
652 }
653 
654 void ICEConnectionObserver::wakeup()
655 {
656     char cChar = 'w';
657     write( nWakeupFiles[1], &cChar, 1 );
658 }
659 
660 void ICEConnectionWorker( void* )
661 {
662 #ifdef USE_SM_EXTENSION
663     while( osl_scheduleThread(ICEConnectionObserver::ICEThread) && ICEConnectionObserver::nConnections )
664     {
665         ICEConnectionObserver::lock();
666         int nConnectionsBefore = ICEConnectionObserver::nConnections;
667         int nBytes = sizeof( struct pollfd )*(nConnectionsBefore+1);
668         struct pollfd* pLocalFD = (struct pollfd*)rtl_allocateMemory( nBytes );
669         rtl_copyMemory( pLocalFD, ICEConnectionObserver::pFilehandles, nBytes );
670         ICEConnectionObserver::unlock();
671 
672         int nRet = poll( pLocalFD,nConnectionsBefore+1,-1 );
673         bool bWakeup = (pLocalFD[0].revents & POLLIN);
674         rtl_freeMemory( pLocalFD );
675 
676         if( nRet < 1 )
677             continue;
678 
679         // clear wakeup pipe
680         if( bWakeup )
681         {
682             char buf[4];
683             while( read( ICEConnectionObserver::nWakeupFiles[0], buf, sizeof( buf ) ) > 0 )
684                 ;
685             SMprintf( "file handles active in wakeup: %d\n", nRet );
686             if( nRet == 1 )
687                 continue;
688         }
689 
690         // check fd's after we obtained the lock
691         ICEConnectionObserver::lock();
692         if( ICEConnectionObserver::nConnections > 0 && ICEConnectionObserver::nConnections == nConnectionsBefore )
693         {
694             nRet = poll( ICEConnectionObserver::pFilehandles+1, ICEConnectionObserver::nConnections, 0 );
695             if( nRet > 0 )
696             {
697                 SMprintf( "IceProcessMessages\n" );
698                 Bool bReply;
699                 for( int i = 0; i < ICEConnectionObserver::nConnections; i++ )
700                     if( ICEConnectionObserver::pFilehandles[i+1].revents & POLLIN )
701                         IceProcessMessages( ICEConnectionObserver::pConnections[i], NULL, &bReply );
702             }
703         }
704         ICEConnectionObserver::unlock();
705     }
706 #endif
707     SMprintf( "shutting donw ICE dispatch thread\n" );
708 }
709 
710 void ICEConnectionObserver::ICEWatchProc(
711 	IceConn connection,
712 	IcePointer,
713 	Bool opening,
714 	IcePointer*
715 	)
716 {
717     // note: this is a callback function for ICE
718     // this implicitly means that a call into ICE lib is calling this
719     // so the ICEMutex MUST already be locked by the caller
720 
721 #ifdef USE_SM_EXTENSION
722     if( opening )
723     {
724         int fd = IceConnectionNumber( connection );
725         nConnections++;
726         pConnections = (IceConn*)rtl_reallocateMemory( pConnections, sizeof( IceConn )*nConnections );
727         pFilehandles = (struct pollfd*)rtl_reallocateMemory( pFilehandles, sizeof( struct pollfd )*(nConnections+1) );
728         pConnections[ nConnections-1 ]		= connection;
729         pFilehandles[ nConnections ].fd		= fd;
730         pFilehandles[ nConnections ].events	= POLLIN;
731         if( nConnections == 1 )
732         {
733             if( ! pipe( nWakeupFiles ) )
734             {
735                 int flags;
736                 pFilehandles[0].fd		= nWakeupFiles[0];
737                 pFilehandles[0].events	= POLLIN;
738                 // set close-on-exec and nonblock descriptor flag.
739                 if ((flags = fcntl (nWakeupFiles[0], F_GETFD)) != -1)
740                 {
741                     flags |= FD_CLOEXEC;
742                     fcntl (nWakeupFiles[0], F_SETFD, flags);
743                 }
744                 if ((flags = fcntl (nWakeupFiles[0], F_GETFL)) != -1)
745                 {
746                     flags |= O_NONBLOCK;
747                     fcntl (nWakeupFiles[0], F_SETFL, flags);
748                 }
749                 // set close-on-exec and nonblock descriptor flag.
750                 if ((flags = fcntl (nWakeupFiles[1], F_GETFD)) != -1)
751                 {
752                     flags |= FD_CLOEXEC;
753                     fcntl (nWakeupFiles[1], F_SETFD, flags);
754                 }
755                 if ((flags = fcntl (nWakeupFiles[1], F_GETFL)) != -1)
756                 {
757                     flags |= O_NONBLOCK;
758                     fcntl (nWakeupFiles[1], F_SETFL, flags);
759                 }
760                 ICEThread = osl_createSuspendedThread( ICEConnectionWorker, NULL );
761                 osl_resumeThread( ICEThread );
762             }
763         }
764     }
765     else
766     {
767         for( int i = 0; i < nConnections; i++ )
768         {
769             if( pConnections[i] == connection )
770             {
771                 if( i < nConnections-1 )
772                 {
773                     rtl_moveMemory( pConnections+i, pConnections+i+1, sizeof( IceConn )*(nConnections-i-1) );
774                     rtl_moveMemory( pFilehandles+i+1, pFilehandles+i+2, sizeof( struct pollfd )*(nConnections-i-1) );
775                 }
776                 nConnections--;
777                 pConnections = (IceConn*)rtl_reallocateMemory( pConnections, sizeof( IceConn )*nConnections );
778                 pFilehandles = (struct pollfd*)rtl_reallocateMemory( pFilehandles, sizeof( struct pollfd )*(nConnections+1) );
779                 break;
780             }
781         }
782         if( nConnections == 0 && ICEThread )
783         {
784             SMprintf( "terminating ICEThread\n" );
785             osl_terminateThread( ICEThread );
786             wakeup();
787             // must release the mutex here
788             osl_releaseMutex( ICEMutex );
789             osl_joinWithThread( ICEThread );
790             osl_destroyThread( ICEThread );
791             close( nWakeupFiles[1] );
792             close( nWakeupFiles[0] );
793             ICEThread = NULL;
794         }
795     }
796     SMprintf( "ICE connection on %d %s\n",
797               IceConnectionNumber( connection ),
798               opening ? "inserted" : "removed" );
799     SMprintf( "Display connection is %d\n", ConnectionNumber( GetX11SalData()->GetDisplay()->GetDisplay() ) );
800 #endif
801 }
802