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