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