1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
10 * $Revision: 1.33.90.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
40 #include <osl/process.h>
41 #include <osl/security.h>
43 #include <tools/prex.h>
44 #include <X11/Xatom.h>
45 #include <tools/postx.h>
47 #include <saldata.hxx>
48 #include <saldisp.hxx>
50 #include <vcl/svapp.hxx>
51 #include <vcl/window.hxx>
54 #include <osl/conditn.h>
56 #define USE_SM_EXTENSION
58 #if OSL_DEBUG_LEVEL > 1
60 static bool bFirstAssert
= true;
63 #if OSL_DEBUG_LEVEL > 1
64 inline void SMprintf( const char* pFormat
, ... )
66 inline void SMprintf( const char*, ... )
69 #if OSL_DEBUG_LEVEL > 1
70 FILE* fp
= fopen( "/tmp/sessionlog.txt", bFirstAssert
? "w" : "a" );
74 va_start( ap
, pFormat
);
75 vfprintf( fp
, pFormat
, ap
);
81 static IceSalSession
* pOneInstance
= NULL
;
83 SalSession
* X11SalInstance::CreateSalSession()
86 pOneInstance
= new IceSalSession();
94 static X11SalFrame
* pOldStyleSaveFrame
= NULL
;
96 IceSalSession::IceSalSession()
100 IceSalSession::~IceSalSession()
102 if( pOneInstance
== this )
106 void IceSalSession::queryInteraction()
108 if( ! SessionManagerClient::queryInteraction() )
110 SalSessionInteractionEvent
aEvent( false );
111 CallCallback( &aEvent
);
115 void IceSalSession::interactionDone()
117 SessionManagerClient::interactionDone();
120 void IceSalSession::saveDone()
122 SessionManagerClient::saveDone();
123 if( pOldStyleSaveFrame
)
125 // note: does nothing if not running in generic plugin
126 X11SalFrame::SaveYourselfDone( pOldStyleSaveFrame
);
130 bool IceSalSession::cancelShutdown()
135 void IceSalSession::handleOldX11SaveYourself( SalFrame
* pFrame
)
138 if( ! pOldStyleSaveFrame
)
140 pOldStyleSaveFrame
= static_cast<X11SalFrame
*>(pFrame
);
143 SalSessionSaveRequestEvent
aEvent( true, false );
144 pOneInstance
->CallCallback( &aEvent
);
149 extern "C" void SAL_CALL
ICEConnectionWorker( void* );
151 class ICEConnectionObserver
153 friend void SAL_CALL
ICEConnectionWorker(void*);
154 static BOOL bIsWatching
;
155 static void ICEWatchProc( IceConn connection
, IcePointer client_data
,
156 Bool opening
, IcePointer
* watch_data
);
158 static struct pollfd
* pFilehandles
;
159 static IceConn
* pConnections
;
160 static int nConnections
;
161 static int nWakeupFiles
[2];
162 static oslMutex ICEMutex
;
163 static oslThread ICEThread
;
166 static void activate();
167 static void deactivate();
169 static void unlock();
170 static void wakeup();
174 SmcConn
SessionManagerClient::aSmcConnection
= NULL
;
175 ByteString
SessionManagerClient::aClientID
;
176 BOOL
ICEConnectionObserver::bIsWatching
= FALSE
;
177 struct pollfd
* ICEConnectionObserver::pFilehandles
= NULL
;
178 IceConn
* ICEConnectionObserver::pConnections
= NULL
;
179 int ICEConnectionObserver::nConnections
= 0;
180 oslMutex
ICEConnectionObserver::ICEMutex
= NULL
;
181 oslThread
ICEConnectionObserver::ICEThread
= NULL
;
182 int ICEConnectionObserver::nWakeupFiles
[2] = { 0, 0 };
185 bool SessionManagerClient::bDocSaveDone
= false;
188 static SmProp
* pSmProps
= NULL
;
189 static SmProp
** ppSmProps
= NULL
;
190 static int nSmProps
= 0;
191 static unsigned char *pSmRestartHint
= NULL
;
194 static void BuildSmPropertyList()
198 ByteString
aExec( SessionManagerClient::getExecName(), osl_getThreadTextEncoding() );
201 pSmProps
= new SmProp
[ nSmProps
];
203 pSmProps
[ 0 ].name
= const_cast<char*>(SmCloneCommand
);
204 pSmProps
[ 0 ].type
= const_cast<char*>(SmLISTofARRAY8
);
205 pSmProps
[ 0 ].num_vals
= 1;
206 pSmProps
[ 0 ].vals
= new SmPropValue
;
207 pSmProps
[ 0 ].vals
->length
= aExec
.Len()+1;
208 pSmProps
[ 0 ].vals
->value
= strdup( aExec
.GetBuffer() );
210 pSmProps
[ 1 ].name
= const_cast<char*>(SmProgram
);
211 pSmProps
[ 1 ].type
= const_cast<char*>(SmARRAY8
);
212 pSmProps
[ 1 ].num_vals
= 1;
213 pSmProps
[ 1 ].vals
= new SmPropValue
;
214 pSmProps
[ 1 ].vals
->length
= aExec
.Len()+1;
215 pSmProps
[ 1 ].vals
->value
= strdup( aExec
.GetBuffer() );
217 pSmProps
[ 2 ].name
= const_cast<char*>(SmRestartCommand
);
218 pSmProps
[ 2 ].type
= const_cast<char*>(SmLISTofARRAY8
);
219 pSmProps
[ 2 ].num_vals
= 3;
220 pSmProps
[ 2 ].vals
= new SmPropValue
[3];
221 pSmProps
[ 2 ].vals
[0].length
= aExec
.Len()+1;
222 pSmProps
[ 2 ].vals
[0].value
= strdup( aExec
.GetBuffer() );
223 ByteString
aRestartOption( "-session=" );
224 aRestartOption
.Append( SessionManagerClient::getSessionID() );
225 pSmProps
[ 2 ].vals
[1].length
= aRestartOption
.Len()+1;
226 pSmProps
[ 2 ].vals
[1].value
= strdup( aRestartOption
.GetBuffer() );
227 ByteString
aRestartOptionNoLogo( "-nologo" );
228 pSmProps
[ 2 ].vals
[2].length
= aRestartOptionNoLogo
.Len()+1;
229 pSmProps
[ 2 ].vals
[2].value
= strdup( aRestartOptionNoLogo
.GetBuffer() );
231 rtl::OUString aUserName
;
233 oslSecurity aSec
= osl_getCurrentSecurity();
236 osl_getUserName( aSec
, &aUserName
.pData
);
237 aUser
= rtl::OUStringToOString( aUserName
, osl_getThreadTextEncoding() );
238 osl_freeSecurityHandle( aSec
);
241 pSmProps
[ 3 ].name
= const_cast<char*>(SmUserID
);
242 pSmProps
[ 3 ].type
= const_cast<char*>(SmARRAY8
);
243 pSmProps
[ 3 ].num_vals
= 1;
244 pSmProps
[ 3 ].vals
= new SmPropValue
;
245 pSmProps
[ 3 ].vals
->value
= strdup( aUser
.getStr() );
246 pSmProps
[ 3 ].vals
->length
= strlen( (char *)pSmProps
[ 3 ].vals
->value
)+1;
248 pSmProps
[ 4 ].name
= const_cast<char*>(SmRestartStyleHint
);
249 pSmProps
[ 4 ].type
= const_cast<char*>(SmCARD8
);
250 pSmProps
[ 4 ].num_vals
= 1;
251 pSmProps
[ 4 ].vals
= new SmPropValue
;
252 pSmProps
[ 4 ].vals
->value
= malloc(1);
253 pSmRestartHint
= (unsigned char *)pSmProps
[ 4 ].vals
->value
;
254 *pSmRestartHint
= SmRestartIfRunning
;
255 pSmProps
[ 4 ].vals
->length
= 1;
257 ppSmProps
= new SmProp
*[ nSmProps
];
258 for( int i
= 0; i
< nSmProps
; i
++ )
259 ppSmProps
[ i
] = &pSmProps
[i
];
263 bool SessionManagerClient::checkDocumentsSaved()
268 IMPL_STATIC_LINK( SessionManagerClient
, SaveYourselfHdl
, void*, EMPTYARG
)
270 SMprintf( "posting save documents event shutdown = %s\n", (pThis
!=0) ? "true" : "false" );
272 static bool bFirstShutdown
=true;
273 if (pThis
!= 0 && bFirstShutdown
) //first shutdown request
275 bFirstShutdown
= false;
277 If we have no actual frames open, e.g. we launched a quickstarter,
278 and then shutdown all our frames leaving just a quickstarter running,
279 then we don't want to launch an empty toplevel frame on the next
280 start. (The job of scheduling the restart of the quick-starter is a
281 task of the quick-starter)
283 *pSmRestartHint
= SmRestartNever
;
284 const std::list
< SalFrame
* >& rFrames
= GetX11SalData()->GetDisplay()->getFrames();
285 for( std::list
< SalFrame
* >::const_iterator it
= rFrames
.begin(); it
!= rFrames
.end(); ++it
)
287 Window
*pWindow
= (*it
)->GetWindow();
288 if (pWindow
&& pWindow
->IsVisible())
290 *pSmRestartHint
= SmRestartIfRunning
;
298 SalSessionSaveRequestEvent
aEvent( pThis
!= 0, false );
299 pOneInstance
->CallCallback( &aEvent
);
307 IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient
, InteractionHdl
, void*, EMPTYARG
)
309 SMprintf( "interaction link\n" );
312 SalSessionInteractionEvent
aEvent( true );
313 pOneInstance
->CallCallback( &aEvent
);
319 IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient
, ShutDownCancelHdl
, void*, EMPTYARG
)
321 SMprintf( "shutdown cancel\n" );
324 SalSessionShutdownCancelEvent aEvent
;
325 pOneInstance
->CallCallback( &aEvent
);
331 void SessionManagerClient::SaveYourselfProc(
340 SMprintf( "Session: save yourself, save_type = %s, shutdown = %s, interact_style = %s, fast = %s\n",
341 save_type
== SmSaveLocal
? "SmcSaveLocal" :
342 ( save_type
== SmSaveGlobal
? "SmcSaveGlobal" :
343 ( save_type
== SmSaveBoth
? "SmcSaveBoth" : "<unknown>" ) ),
344 shutdown
? "true" : "false",
345 interact_style
== SmInteractStyleNone
? "SmInteractStyleNone" :
346 ( interact_style
== SmInteractStyleErrors
? "SmInteractStyleErrors" :
347 ( interact_style
== SmInteractStyleAny
? "SmInteractStyleAny" : "<unknown>" ) ),
348 false ? "true" : "false"
350 BuildSmPropertyList();
351 #ifdef USE_SM_EXTENSION
352 bDocSaveDone
= false;
353 /* #i49875# some session managers send a "die" message if the
354 * saveDone does not come early enough for their convenience
355 * this can occasionally happen on startup, especially the first
356 * startup. So shortcut the "not shutting down" case since the
357 * upper layers are currently not interested in that event anyway.
361 SessionManagerClient::saveDone();
364 Application::PostUserEvent( STATIC_LINK( (void*)(shutdown
? 0xffffffff : 0x0), SessionManagerClient
, SaveYourselfHdl
) );
365 SMprintf( "waiting for save yourself event to be processed\n" );
369 IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient
, ShutDownHdl
, void*, EMPTYARG
)
371 const std::list
< SalFrame
* >& rFrames
= GetX11SalData()->GetDisplay()->getFrames();
372 SMprintf( rFrames
.begin() != rFrames
.end() ? "shutdown on first frame\n" : "shutdown event but no frame\n" );
373 if( rFrames
.begin() != rFrames
.end() )
374 rFrames
.front()->CallCallback( SALEVENT_SHUTDOWN
, 0 );
378 void SessionManagerClient::DieProc(
383 SMprintf( "Session: die\n" );
384 if( connection
== aSmcConnection
)
386 Application::PostUserEvent( STATIC_LINK( NULL
, SessionManagerClient
, ShutDownHdl
) );
387 SMprintf( "waiting for shutdown event to be processed\n" );
391 void SessionManagerClient::SaveCompleteProc(
396 SMprintf( "Session: save complete\n" );
399 void SessionManagerClient::ShutdownCanceledProc(
403 SMprintf( "Session: shutdown canceled\n" );
404 if( connection
== aSmcConnection
)
405 Application::PostUserEvent( STATIC_LINK( NULL
, SessionManagerClient
, ShutDownCancelHdl
) );
408 void SessionManagerClient::InteractProc(
412 SMprintf( "Session: interaction request completed\n" );
413 if( connection
== aSmcConnection
)
414 Application::PostUserEvent( STATIC_LINK( NULL
, SessionManagerClient
, InteractionHdl
) );
417 void SessionManagerClient::saveDone()
421 ICEConnectionObserver::lock();
422 SmcSetProperties( aSmcConnection
, nSmProps
, ppSmProps
);
423 SmcSaveYourselfDone( aSmcConnection
, True
);
424 SMprintf( "sent SaveYourselfDone SmRestartHint of %d\n", *pSmRestartHint
);
426 ICEConnectionObserver::unlock();
431 void SessionManagerClient::open()
433 static SmcCallbacks aCallbacks
;
435 #ifdef USE_SM_EXTENSION
436 // this is the way Xt does it, so we can too
437 if( ! aSmcConnection
&& getenv( "SESSION_MANAGER" ) )
440 ICEConnectionObserver::activate();
441 ICEConnectionObserver::lock();
443 char* pClientID
= NULL
;
444 const ByteString
& rPrevId( getPreviousSessionID() );
446 aCallbacks
.save_yourself
.callback
= SaveYourselfProc
;
447 aCallbacks
.save_yourself
.client_data
= NULL
;
448 aCallbacks
.die
.callback
= DieProc
;
449 aCallbacks
.die
.client_data
= NULL
;
450 aCallbacks
.save_complete
.callback
= SaveCompleteProc
;
451 aCallbacks
.save_complete
.client_data
= NULL
;
452 aCallbacks
.shutdown_cancelled
.callback
= ShutdownCanceledProc
;
453 aCallbacks
.shutdown_cancelled
.client_data
= NULL
;
454 aSmcConnection
= SmcOpenConnection( NULL
,
458 SmcSaveYourselfProcMask
|
460 SmcSaveCompleteProcMask
|
461 SmcShutdownCancelledProcMask
,
463 rPrevId
.Len() ? const_cast<char*>(rPrevId
.GetBuffer()) : NULL
,
467 if( ! aSmcConnection
)
468 SMprintf( "SmcOpenConnection failed: %s\n", aErrBuf
);
470 SMprintf( "SmcOpenConnection succeeded, client ID is \"%s\"\n", pClientID
);
471 aClientID
= ByteString( pClientID
);
474 ICEConnectionObserver::unlock();
476 SalDisplay
* pDisp
= GetX11SalData()->GetDisplay();
477 if( pDisp
->GetDrawable( pDisp
->GetDefaultScreenNumber() ) && aClientID
.Len() )
479 XChangeProperty( pDisp
->GetDisplay(),
480 pDisp
->GetDrawable( pDisp
->GetDefaultScreenNumber() ),
481 XInternAtom( pDisp
->GetDisplay(), "SM_CLIENT_ID", False
),
485 (unsigned char*)aClientID
.GetBuffer(),
490 else if( ! aSmcConnection
)
491 SMprintf( "no SESSION_MANAGER\n" );
495 const ByteString
& SessionManagerClient::getSessionID()
500 void SessionManagerClient::close()
504 #ifdef USE_SM_EXTENSION
505 ICEConnectionObserver::lock();
506 SMprintf( "attempting SmcCloseConnection\n" );
507 SmcCloseConnection( aSmcConnection
, 0, NULL
);
508 SMprintf( "SmcConnection closed\n" );
509 ICEConnectionObserver::unlock();
510 ICEConnectionObserver::deactivate();
512 aSmcConnection
= NULL
;
516 bool SessionManagerClient::queryInteraction()
521 ICEConnectionObserver::lock();
522 if( SmcInteractRequest( aSmcConnection
, SmDialogNormal
, InteractProc
, NULL
) )
524 ICEConnectionObserver::unlock();
529 void SessionManagerClient::interactionDone()
533 ICEConnectionObserver::lock();
534 SmcInteractDone( aSmcConnection
, False
);
535 ICEConnectionObserver::unlock();
540 String
SessionManagerClient::getExecName()
542 rtl::OUString aExec
, aSysExec
;
543 osl_getExecutableFile( &aExec
.pData
);
544 osl_getSystemPathFromFileURL( aExec
.pData
, &aSysExec
.pData
);
546 int nPos
= aSysExec
.indexOf( rtl::OUString::createFromAscii( ".bin" ) );
548 aSysExec
= aSysExec
.copy( 0, nPos
);
553 const ByteString
& SessionManagerClient::getPreviousSessionID()
555 static ByteString aPrevId
;
557 int nCommands
= osl_getCommandArgCount();
558 for( int i
= 0; i
< nCommands
; i
++ )
560 ::rtl::OUString aArg
;
561 osl_getCommandArg( i
, &aArg
.pData
);
562 if( aArg
.compareToAscii( "-session=", 9 ) == 0 )
564 aPrevId
= ByteString( ::rtl::OUStringToOString( aArg
.copy( 9 ), osl_getThreadTextEncoding() ) );
568 SMprintf( "previous ID = \"%s\"\n", aPrevId
.GetBuffer() );
572 void ICEConnectionObserver::lock()
574 osl_acquireMutex( ICEMutex
);
577 void ICEConnectionObserver::unlock()
579 osl_releaseMutex( ICEMutex
);
582 void ICEConnectionObserver::activate()
586 nWakeupFiles
[0] = nWakeupFiles
[1] = 0;
587 ICEMutex
= osl_createMutex();
589 #ifdef USE_SM_EXTENSION
590 IceAddConnectionWatch( ICEWatchProc
, NULL
);
595 void ICEConnectionObserver::deactivate()
601 #ifdef USE_SM_EXTENSION
602 IceRemoveConnectionWatch( ICEWatchProc
, NULL
);
607 osl_terminateThread( ICEThread
);
613 osl_joinWithThread( ICEThread
);
614 osl_destroyThread( ICEThread
);
615 close( nWakeupFiles
[1] );
616 close( nWakeupFiles
[0] );
619 osl_destroyMutex( ICEMutex
);
624 void ICEConnectionObserver::wakeup()
627 write( nWakeupFiles
[1], &cChar
, 1 );
630 void ICEConnectionWorker( void* )
632 #ifdef USE_SM_EXTENSION
633 while( osl_scheduleThread(ICEConnectionObserver::ICEThread
) && ICEConnectionObserver::nConnections
)
635 ICEConnectionObserver::lock();
636 int nConnectionsBefore
= ICEConnectionObserver::nConnections
;
637 int nBytes
= sizeof( struct pollfd
)*(nConnectionsBefore
+1);
638 struct pollfd
* pLocalFD
= (struct pollfd
*)rtl_allocateMemory( nBytes
);
639 rtl_copyMemory( pLocalFD
, ICEConnectionObserver::pFilehandles
, nBytes
);
640 ICEConnectionObserver::unlock();
642 int nRet
= poll( pLocalFD
,nConnectionsBefore
+1,-1 );
643 bool bWakeup
= (pLocalFD
[0].revents
& POLLIN
);
644 rtl_freeMemory( pLocalFD
);
653 while( read( ICEConnectionObserver::nWakeupFiles
[0], buf
, sizeof( buf
) ) > 0 )
655 SMprintf( "file handles active in wakeup: %d\n", nRet
);
660 // check fd's after we obtained the lock
661 ICEConnectionObserver::lock();
662 if( ICEConnectionObserver::nConnections
> 0 && ICEConnectionObserver::nConnections
== nConnectionsBefore
)
664 nRet
= poll( ICEConnectionObserver::pFilehandles
+1, ICEConnectionObserver::nConnections
, 0 );
667 SMprintf( "IceProcessMessages\n" );
669 for( int i
= 0; i
< ICEConnectionObserver::nConnections
; i
++ )
670 if( ICEConnectionObserver::pFilehandles
[i
+1].revents
& POLLIN
)
671 IceProcessMessages( ICEConnectionObserver::pConnections
[i
], NULL
, &bReply
);
674 ICEConnectionObserver::unlock();
677 SMprintf( "shutting donw ICE dispatch thread\n" );
680 void ICEConnectionObserver::ICEWatchProc(
687 // note: this is a callback function for ICE
688 // this implicitly means that a call into ICE lib is calling this
689 // so the ICEMutex MUST already be locked by the caller
691 #ifdef USE_SM_EXTENSION
694 int fd
= IceConnectionNumber( connection
);
696 pConnections
= (IceConn
*)rtl_reallocateMemory( pConnections
, sizeof( IceConn
)*nConnections
);
697 pFilehandles
= (struct pollfd
*)rtl_reallocateMemory( pFilehandles
, sizeof( struct pollfd
)*(nConnections
+1) );
698 pConnections
[ nConnections
-1 ] = connection
;
699 pFilehandles
[ nConnections
].fd
= fd
;
700 pFilehandles
[ nConnections
].events
= POLLIN
;
701 if( nConnections
== 1 )
703 if( ! pipe( nWakeupFiles
) )
706 pFilehandles
[0].fd
= nWakeupFiles
[0];
707 pFilehandles
[0].events
= POLLIN
;
708 // set close-on-exec and nonblock descriptor flag.
709 if ((flags
= fcntl (nWakeupFiles
[0], F_GETFD
)) != -1)
712 fcntl (nWakeupFiles
[0], F_SETFD
, flags
);
714 if ((flags
= fcntl (nWakeupFiles
[0], F_GETFL
)) != -1)
717 fcntl (nWakeupFiles
[0], F_SETFL
, flags
);
719 // set close-on-exec and nonblock descriptor flag.
720 if ((flags
= fcntl (nWakeupFiles
[1], F_GETFD
)) != -1)
723 fcntl (nWakeupFiles
[1], F_SETFD
, flags
);
725 if ((flags
= fcntl (nWakeupFiles
[1], F_GETFL
)) != -1)
728 fcntl (nWakeupFiles
[1], F_SETFL
, flags
);
730 ICEThread
= osl_createSuspendedThread( ICEConnectionWorker
, NULL
);
731 osl_resumeThread( ICEThread
);
737 for( int i
= 0; i
< nConnections
; i
++ )
739 if( pConnections
[i
] == connection
)
741 if( i
< nConnections
-1 )
743 rtl_moveMemory( pConnections
+i
, pConnections
+i
+1, sizeof( IceConn
)*(nConnections
-i
-1) );
744 rtl_moveMemory( pFilehandles
+i
+1, pFilehandles
+i
+2, sizeof( struct pollfd
)*(nConnections
-i
-1) );
747 pConnections
= (IceConn
*)rtl_reallocateMemory( pConnections
, sizeof( IceConn
)*nConnections
);
748 pFilehandles
= (struct pollfd
*)rtl_reallocateMemory( pFilehandles
, sizeof( struct pollfd
)*(nConnections
+1) );
752 if( nConnections
== 0 && ICEThread
)
754 SMprintf( "terminating ICEThread\n" );
755 osl_terminateThread( ICEThread
);
757 // must release the mutex here
758 osl_releaseMutex( ICEMutex
);
759 osl_joinWithThread( ICEThread
);
760 osl_destroyThread( ICEThread
);
761 close( nWakeupFiles
[1] );
762 close( nWakeupFiles
[0] );
766 SMprintf( "ICE connection on %d %s\n",
767 IceConnectionNumber( connection
),
768 opening
? "inserted" : "removed" );
769 SMprintf( "Display connection is %d\n", ConnectionNumber( GetX11SalData()->GetDisplay()->GetDisplay() ) );