defer finding dialog parent until we need it
[LibreOffice.git] / vcl / unx / generic / app / sm.cxx
blobb38ea0184d82771b162609a097a50abf296608e1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <memory>
21 #include <sal/config.h>
23 #include <cassert>
25 #include <string.h>
26 #include <unistd.h>
27 #include <poll.h>
28 #include <fcntl.h>
30 #include <rtl/strbuf.hxx>
31 #include <sal/log.hxx>
33 #include <rtl/process.h>
34 #include <osl/security.h>
35 #include <osl/diagnose.h>
37 #include <X11/Xlib.h>
38 #include <X11/Xatom.h>
40 #include <unx/sm.hxx>
41 #include <unx/saldisp.hxx>
42 #include <unx/salinst.h>
44 #include <vcl/svapp.hxx>
45 #include <vcl/window.hxx>
47 #include <salframe.hxx>
48 #include <salsession.hxx>
50 namespace {
52 class IceSalSession : public SalSession
54 public:
55 IceSalSession() {}
57 private:
58 virtual ~IceSalSession() override {}
60 virtual void queryInteraction() override;
61 virtual void interactionDone() override;
62 virtual void saveDone() override;
63 virtual bool cancelShutdown() override;
68 std::unique_ptr<SalSession> X11SalInstance::CreateSalSession()
70 SAL_INFO("vcl.sm", "X11SalInstance::CreateSalSession");
72 std::unique_ptr<SalSession> p(new IceSalSession);
73 SessionManagerClient::open(p.get());
74 return p;
77 void IceSalSession::queryInteraction()
79 SAL_INFO("vcl.sm", "IceSalSession::queryInteraction");
81 if( ! SessionManagerClient::queryInteraction() )
83 SAL_INFO("vcl.sm.debug", " call SalSessionInteractionEvent");
84 SalSessionInteractionEvent aEvent( false );
85 CallCallback( &aEvent );
89 void IceSalSession::interactionDone()
91 SAL_INFO("vcl.sm", "IceSalSession::interactionDone");
93 SessionManagerClient::interactionDone( false );
96 void IceSalSession::saveDone()
98 SAL_INFO("vcl.sm", "IceSalSession::saveDone");
100 SessionManagerClient::saveDone();
103 bool IceSalSession::cancelShutdown()
105 SAL_INFO("vcl.sm", "IceSalSession::cancelShutdown");
107 SessionManagerClient::interactionDone( true );
108 return false;
111 extern "C" {
113 static void ICEWatchProc(
114 IceConn ice_conn, IcePointer client_data, Bool opening,
115 IcePointer * watch_data);
117 static void ICEConnectionWorker(void * data);
121 class ICEConnectionObserver
123 friend void ICEWatchProc(IceConn, IcePointer, Bool, IcePointer *);
125 friend void ICEConnectionWorker(void *);
127 struct pollfd* m_pFilehandles;
128 int m_nConnections;
129 IceConn* m_pConnections;
130 int m_nWakeupFiles[2];
131 oslThread m_ICEThread;
132 IceIOErrorHandler m_origIOErrorHandler;
133 IceErrorHandler m_origErrorHandler;
135 void wakeup();
137 public:
138 osl::Mutex m_ICEMutex;
140 ICEConnectionObserver()
141 : m_pFilehandles(nullptr)
142 , m_nConnections(0)
143 , m_pConnections(nullptr)
144 , m_ICEThread(nullptr)
145 , m_origIOErrorHandler(nullptr)
146 , m_origErrorHandler(nullptr)
148 SAL_INFO("vcl.sm", "ICEConnectionObserver::ICEConnectionObserver");
150 m_nWakeupFiles[0] = m_nWakeupFiles[1] = 0;
153 void activate();
154 void deactivate();
155 void terminate(oslThread iceThread);
158 SalSession * SessionManagerClient::m_pSession = nullptr;
159 std::unique_ptr< ICEConnectionObserver >
160 SessionManagerClient::m_xICEConnectionObserver;
161 SmcConn SessionManagerClient::m_pSmcConnection = nullptr;
162 OString SessionManagerClient::m_aClientID = ""_ostr;
163 OString SessionManagerClient::m_aTimeID = ""_ostr;
164 OString SessionManagerClient::m_aClientTimeID = ""_ostr;
165 bool SessionManagerClient::m_bDocSaveDone = false; // HACK
167 extern "C" {
169 static void IgnoreIceErrors(
170 SAL_UNUSED_PARAMETER IceConn, SAL_UNUSED_PARAMETER Bool,
171 SAL_UNUSED_PARAMETER int, SAL_UNUSED_PARAMETER unsigned long,
172 SAL_UNUSED_PARAMETER int, SAL_UNUSED_PARAMETER int,
173 SAL_UNUSED_PARAMETER IcePointer)
176 static void IgnoreIceIOErrors(SAL_UNUSED_PARAMETER IceConn) {}
180 static SmProp* pSmProps = nullptr;
181 static SmProp** ppSmProps = nullptr;
182 static char ** ppSmDel = nullptr;
184 static int nSmProps = 0;
185 static int nSmDel = 0;
186 static unsigned char *pSmRestartHint = nullptr;
189 enum { eCloneCommand, eProgram, eRestartCommand, eUserId, eRestartStyleHint };
190 enum { eDiscardCommand };
193 static void BuildSmPropertyList()
195 SAL_INFO("vcl.sm", "BuildSmPropertyList");
197 if( ! pSmProps )
199 nSmProps = 5;
200 nSmDel = 1;
201 pSmProps = new SmProp[ nSmProps ];
202 ppSmProps = new SmProp*[ nSmProps ];
203 ppSmDel = new char*[ nSmDel ];
206 OString aExec(OUStringToOString(SessionManagerClient::getExecName(), osl_getThreadTextEncoding()));
208 pSmProps[ eCloneCommand ].name = const_cast<char*>(SmCloneCommand);
209 pSmProps[ eCloneCommand ].type = const_cast<char*>(SmLISTofARRAY8);
210 pSmProps[ eCloneCommand ].num_vals = 1;
211 pSmProps[ eCloneCommand ].vals = new SmPropValue;
212 pSmProps[ eCloneCommand ].vals->length = aExec.getLength()+1;
213 pSmProps[ eCloneCommand ].vals->value = strdup( aExec.getStr() );
215 pSmProps[ eProgram ].name = const_cast<char*>(SmProgram);
216 pSmProps[ eProgram ].type = const_cast<char*>(SmARRAY8);
217 pSmProps[ eProgram ].num_vals = 1;
218 pSmProps[ eProgram ].vals = new SmPropValue;
219 pSmProps[ eProgram ].vals->length = aExec.getLength()+1;
220 pSmProps[ eProgram ].vals->value = strdup( aExec.getStr() );
222 pSmProps[ eRestartCommand ].name = const_cast<char*>(SmRestartCommand);
223 pSmProps[ eRestartCommand ].type = const_cast<char*>(SmLISTofARRAY8);
224 pSmProps[ eRestartCommand ].num_vals = 3;
225 pSmProps[ eRestartCommand ].vals = new SmPropValue[3];
226 pSmProps[ eRestartCommand ].vals[0].length = aExec.getLength()+1;
227 pSmProps[ eRestartCommand ].vals[0].value = strdup( aExec.getStr() );
228 OString aRestartOption = "--session=" + SessionManagerClient::getSessionID();
229 pSmProps[ eRestartCommand ].vals[1].length = aRestartOption.getLength()+1;
230 pSmProps[ eRestartCommand ].vals[1].value = strdup(aRestartOption.getStr());
231 OString aRestartOptionNoLogo("--nologo"_ostr);
232 pSmProps[ eRestartCommand ].vals[2].length = aRestartOptionNoLogo.getLength()+1;
233 pSmProps[ eRestartCommand ].vals[2].value = strdup(aRestartOptionNoLogo.getStr());
235 OUString aUserName;
236 OString aUser;
237 oslSecurity aSec = osl_getCurrentSecurity();
238 if( aSec )
240 osl_getUserName( aSec, &aUserName.pData );
241 aUser = OUStringToOString( aUserName, osl_getThreadTextEncoding() );
242 osl_freeSecurityHandle( aSec );
245 pSmProps[ eUserId ].name = const_cast<char*>(SmUserID);
246 pSmProps[ eUserId ].type = const_cast<char*>(SmARRAY8);
247 pSmProps[ eUserId ].num_vals = 1;
248 pSmProps[ eUserId ].vals = new SmPropValue;
249 pSmProps[ eUserId ].vals->value = strdup( aUser.getStr() );
250 pSmProps[ eUserId ].vals->length = rtl_str_getLength( static_cast<char *>(pSmProps[ 3 ].vals->value) )+1;
252 pSmProps[ eRestartStyleHint ].name = const_cast<char*>(SmRestartStyleHint);
253 pSmProps[ eRestartStyleHint ].type = const_cast<char*>(SmCARD8);
254 pSmProps[ eRestartStyleHint ].num_vals = 1;
255 pSmProps[ eRestartStyleHint ].vals = new SmPropValue;
256 pSmProps[ eRestartStyleHint ].vals->value = malloc(1);
257 pSmRestartHint = static_cast<unsigned char *>(pSmProps[ 4 ].vals->value);
258 *pSmRestartHint = SmRestartIfRunning;
259 pSmProps[ eRestartStyleHint ].vals->length = 1;
261 for( int i = 0; i < nSmProps; i++ )
262 ppSmProps[ i ] = &pSmProps[i];
264 ppSmDel[eDiscardCommand] = const_cast<char*>(SmDiscardCommand);
267 bool SessionManagerClient::checkDocumentsSaved()
269 SAL_INFO("vcl.sm", "SessionManagerClient::checkDocumentsSaved");
271 SAL_INFO("vcl.sm.debug", " m_bcheckDocumentsSaved = " << (m_bDocSaveDone ? "true" : "false" ));
272 return m_bDocSaveDone;
275 IMPL_STATIC_LINK( SessionManagerClient, SaveYourselfHdl, void*, pStateVal, void )
277 SAL_INFO("vcl.sm", "SessionManagerClient, SaveYourselfHdl");
279 // Decode argument smuggled in as void*:
280 sal_uIntPtr nStateVal = reinterpret_cast< sal_uIntPtr >(pStateVal);
281 bool shutdown = nStateVal != 0;
283 static bool bFirstShutdown=true;
285 SAL_INFO("vcl.sm.debug", " shutdown = " << (shutdown ? "true" : "false" ) <<
286 ", bFirstShutdown = " << (bFirstShutdown ? "true" : "false" ));
287 if (shutdown && bFirstShutdown) //first shutdown request
289 bFirstShutdown = false;
291 If we have no actual frames open, e.g. we launched a quickstarter,
292 and then shutdown all our frames leaving just a quickstarter running,
293 then we don't want to launch an empty toplevel frame on the next
294 start. (The job of scheduling the restart of the quick-starter is a
295 task of the quick-starter)
297 *pSmRestartHint = SmRestartNever;
298 for (auto pSalFrame : vcl_sal::getSalDisplay(GetGenericUnixSalData())->getFrames() )
300 vcl::Window *pWindow = pSalFrame->GetWindow();
301 if (pWindow && pWindow->IsVisible())
303 *pSmRestartHint = SmRestartIfRunning;
304 SAL_INFO("vcl.sm.debug", " pSmRestartHint = SmRestartIfRunning");
305 break;
310 if( m_pSession )
312 SalSessionSaveRequestEvent aEvent( shutdown );
313 m_pSession->CallCallback( &aEvent );
315 else
316 saveDone();
319 IMPL_STATIC_LINK_NOARG( SessionManagerClient, InteractionHdl, void*, void )
321 SAL_INFO("vcl.sm", "SessionManagerClient, InteractionHdl");
323 if( m_pSession )
325 SalSessionInteractionEvent aEvent( true );
326 m_pSession->CallCallback( &aEvent );
330 IMPL_STATIC_LINK_NOARG( SessionManagerClient, ShutDownCancelHdl, void*, void )
332 SAL_INFO("vcl.sm", "SessionManagerClient, ShutDownCancelHdl");
334 if( m_pSession )
336 SalSessionShutdownCancelEvent aEvent;
337 m_pSession->CallCallback( &aEvent );
341 void SessionManagerClient::SaveYourselfProc(
342 SmcConn,
343 SmPointer,
344 int save_type,
345 Bool shutdown,
346 int interact_style,
347 Bool
350 SAL_INFO("vcl.sm", "SessionManagerClient::SaveYourselfProc");
352 TimeValue now;
353 osl_getSystemTime(&now);
355 SAL_INFO("vcl.sm", " save_type = " << ((save_type == SmSaveLocal ) ? "local" :
356 (save_type == SmSaveGlobal) ? "global" : "both") <<
357 ", shutdown = " << (shutdown ? "true" : "false" ) <<
358 ", interact_style = " << ((interact_style == SmInteractStyleNone) ? "SmInteractStyleNone" :
359 (interact_style == SmInteractStyleErrors) ? "SmInteractStyleErrors" :
360 "SmInteractStyleAny"));
361 char num[100];
362 snprintf(num, sizeof(num), "_%" SAL_PRIuUINT32 "_%" SAL_PRIuUINT32, now.Seconds, (now.Nanosec / 1001));
363 m_aTimeID = OString(num);
365 BuildSmPropertyList();
367 SmcSetProperties( m_pSmcConnection, 1, &ppSmProps[ eProgram ] );
368 SmcSetProperties( m_pSmcConnection, 1, &ppSmProps[ eUserId ] );
371 m_bDocSaveDone = false;
372 /* #i49875# some session managers send a "die" message if the
373 * saveDone does not come early enough for their convenience
374 * this can occasionally happen on startup, especially the first
375 * startup. So shortcut the "not shutting down" case since the
376 * upper layers are currently not interested in that event anyway.
378 if( ! shutdown )
380 SessionManagerClient::saveDone();
381 return;
383 // Smuggle argument in as void*:
384 sal_uIntPtr nStateVal = shutdown;
385 Application::PostUserEvent( LINK( nullptr, SessionManagerClient, SaveYourselfHdl ), reinterpret_cast< void * >(nStateVal) );
388 IMPL_STATIC_LINK_NOARG( SessionManagerClient, ShutDownHdl, void*, void )
390 SAL_INFO("vcl.sm", "SessionManagerClient, ShutDownHdl");
392 if( m_pSession )
394 SalSessionQuitEvent aEvent;
395 m_pSession->CallCallback( &aEvent );
398 SalFrame *pAnyFrame = vcl_sal::getSalDisplay(GetGenericUnixSalData())->anyFrame();
399 SAL_INFO("vcl.sm.debug", " rFrames.empty() = " << (pAnyFrame ? "true" : "false"));
400 if( pAnyFrame )
401 pAnyFrame->CallCallback( SalEvent::Shutdown, nullptr );
404 void SessionManagerClient::DieProc(
405 SmcConn connection,
406 SmPointer
409 SAL_INFO("vcl.sm", "SessionManagerClient::DieProc");
411 if( connection == m_pSmcConnection )
413 SAL_INFO("vcl.sm.debug", " connection == m_pSmcConnection" );
414 Application::PostUserEvent( LINK( nullptr, SessionManagerClient, ShutDownHdl ) );
418 void SessionManagerClient::SaveCompleteProc(
419 SmcConn,
420 SmPointer
423 SAL_INFO("vcl.sm", "SessionManagerClient::SaveCompleteProc");
426 void SessionManagerClient::ShutdownCanceledProc(
427 SmcConn connection,
428 SmPointer )
430 SAL_INFO("vcl.sm", "SessionManagerClient::ShutdownCanceledProc" );
432 SAL_INFO("vcl.sm.debug", " connection == m_pSmcConnection = " << (( connection == m_pSmcConnection ) ? "true" : "false"));
433 if( connection == m_pSmcConnection )
434 Application::PostUserEvent( LINK( nullptr, SessionManagerClient, ShutDownCancelHdl ) );
437 void SessionManagerClient::InteractProc(
438 SmcConn connection,
439 SmPointer )
441 SAL_INFO("vcl.sm", "SessionManagerClient::InteractProc" );
443 SAL_INFO("vcl.sm.debug", " connection == m_pSmcConnection = " << (( connection == m_pSmcConnection ) ? "true" : "false"));
444 if( connection == m_pSmcConnection )
445 Application::PostUserEvent( LINK( nullptr, SessionManagerClient, InteractionHdl ) );
448 void SessionManagerClient::saveDone()
450 SAL_INFO("vcl.sm", "SessionManagerClient::saveDone");
452 if( !m_pSmcConnection )
453 return;
455 assert(m_xICEConnectionObserver);
456 osl::MutexGuard g(m_xICEConnectionObserver->m_ICEMutex);
457 //SmcSetProperties( m_pSmcConnection, 1, &ppSmProps[ eCloneCommand ] );
458 // this message-handling is now equal to kate and plasma desktop
459 SmcSetProperties( m_pSmcConnection, 1, &ppSmProps[ eRestartCommand ] );
460 SmcDeleteProperties( m_pSmcConnection, 1, &ppSmDel[ eDiscardCommand ] );
461 SmcSetProperties( m_pSmcConnection, 1, &ppSmProps[ eRestartStyleHint ] );
463 SmcSaveYourselfDone( m_pSmcConnection, True );
464 SAL_INFO("vcl.sm.debug", " sent SmRestartHint = " << (*pSmRestartHint) );
465 m_bDocSaveDone = true;
468 void SessionManagerClient::open(SalSession * pSession)
470 SAL_INFO("vcl.sm", "SessionManagerClient::open");
472 assert(!m_pSession && !m_xICEConnectionObserver && !m_pSmcConnection);
473 // must only be called once
474 m_pSession = pSession;
475 // This is the way Xt does it, so we can too:
476 if( getenv( "SESSION_MANAGER" ) )
478 SAL_INFO("vcl.sm.debug", " getenv( SESSION_MANAGER ) = true");
479 m_xICEConnectionObserver.reset(new ICEConnectionObserver);
480 m_xICEConnectionObserver->activate();
483 osl::MutexGuard g(m_xICEConnectionObserver->m_ICEMutex);
485 static SmcCallbacks aCallbacks; // does this need to be static?
486 aCallbacks.save_yourself.callback = SaveYourselfProc;
487 aCallbacks.save_yourself.client_data = nullptr;
488 aCallbacks.die.callback = DieProc;
489 aCallbacks.die.client_data = nullptr;
490 aCallbacks.save_complete.callback = SaveCompleteProc;
491 aCallbacks.save_complete.client_data = nullptr;
492 aCallbacks.shutdown_cancelled.callback = ShutdownCanceledProc;
493 aCallbacks.shutdown_cancelled.client_data = nullptr;
494 OString aPrevId(getPreviousSessionID());
495 char* pClientID = nullptr;
496 char aErrBuf[1024];
497 m_pSmcConnection = SmcOpenConnection( nullptr,
498 nullptr,
499 SmProtoMajor,
500 SmProtoMinor,
501 SmcSaveYourselfProcMask |
502 SmcDieProcMask |
503 SmcSaveCompleteProcMask |
504 SmcShutdownCancelledProcMask ,
505 &aCallbacks,
506 aPrevId.isEmpty() ? nullptr : const_cast<char*>(aPrevId.getStr()),
507 &pClientID,
508 sizeof( aErrBuf ),
509 aErrBuf );
510 if( !m_pSmcConnection )
511 SAL_INFO("vcl.sm.debug", " SmcOpenConnection failed: " << aErrBuf);
512 else
513 SAL_INFO("vcl.sm.debug", " SmcOpenConnection succeeded, client ID is " << pClientID );
515 if (pClientID)
516 m_aClientID = OString(pClientID);
517 free( pClientID );
518 pClientID = nullptr;
521 SalDisplay* pDisp = vcl_sal::getSalDisplay(GetGenericUnixSalData());
522 if( pDisp->GetDrawable(pDisp->GetDefaultXScreen()) && !m_aClientID.isEmpty() )
524 SAL_INFO("vcl.sm.debug", " SmcOpenConnection open: pDisp->GetDrawable = true");
525 XChangeProperty( pDisp->GetDisplay(),
526 pDisp->GetDrawable( pDisp->GetDefaultXScreen() ),
527 XInternAtom( pDisp->GetDisplay(), "SM_CLIENT_ID", False ),
528 XA_STRING,
530 PropModeReplace,
531 reinterpret_cast<unsigned char const *>(m_aClientID.getStr()),
532 m_aClientID.getLength()
536 else
538 SAL_INFO("vcl.sm.debug", " getenv( SESSION_MANAGER ) = false");
542 const OString& SessionManagerClient::getSessionID()
544 SAL_INFO("vcl.sm", "SessionManagerClient::getSessionID");
546 m_aClientTimeID = m_aClientID + m_aTimeID;
548 SAL_INFO("vcl.sm", " SessionID = " << m_aClientTimeID);
550 return m_aClientTimeID;
553 void SessionManagerClient::close()
555 SAL_INFO("vcl.sm", "SessionManagerClient::close");
557 if( !m_pSmcConnection )
558 return;
560 SAL_INFO("vcl.sm.debug", " attempting SmcCloseConnection");
561 assert(m_xICEConnectionObserver);
563 osl::MutexGuard g(m_xICEConnectionObserver->m_ICEMutex);
564 SmcCloseConnection( m_pSmcConnection, 0, nullptr );
565 SAL_INFO("vcl.sm", " SmcCloseConnection closed");
567 m_xICEConnectionObserver->deactivate();
568 m_xICEConnectionObserver.reset();
569 m_pSmcConnection = nullptr;
572 bool SessionManagerClient::queryInteraction()
574 SAL_INFO("vcl.sm", "SessionManagerClient::queryInteraction");
576 bool bRet = false;
577 if( m_pSmcConnection )
579 assert(m_xICEConnectionObserver);
580 osl::MutexGuard g(m_xICEConnectionObserver->m_ICEMutex);
581 SAL_INFO("vcl.sm.debug", " SmcInteractRequest" );
582 if( SmcInteractRequest( m_pSmcConnection, SmDialogNormal, InteractProc, nullptr ) )
583 bRet = true;
585 return bRet;
588 void SessionManagerClient::interactionDone( bool bCancelShutdown )
590 SAL_INFO("vcl.sm", "SessionManagerClient::interactionDone");
592 if( m_pSmcConnection )
594 assert(m_xICEConnectionObserver);
595 osl::MutexGuard g(m_xICEConnectionObserver->m_ICEMutex);
596 SAL_INFO("vcl.sm.debug", " SmcInteractDone = " << (bCancelShutdown ? "true" : "false") );
597 SmcInteractDone( m_pSmcConnection, bCancelShutdown ? True : False );
601 OUString SessionManagerClient::getExecName()
603 SAL_INFO("vcl.sm", "SessionManagerClient::getExecName");
605 OUString aExec, aSysExec;
606 osl_getExecutableFile( &aExec.pData );
607 osl_getSystemPathFromFileURL( aExec.pData, &aSysExec.pData );
609 if( aSysExec.endsWith(".bin") )
610 aSysExec = aSysExec.copy( 0, aSysExec.getLength() - RTL_CONSTASCII_LENGTH(".bin") );
612 SAL_INFO("vcl.sm.debug", " aSysExec = " << aSysExec);
613 return aSysExec;
616 OString SessionManagerClient::getPreviousSessionID()
618 SAL_INFO("vcl.sm", "SessionManagerClient::getPreviousSessionID");
620 OString aPrevId;
622 sal_uInt32 n = rtl_getAppCommandArgCount();
623 for (sal_uInt32 i = 0; i != n; ++i)
625 OUString aArg;
626 rtl_getAppCommandArg( i, &aArg.pData );
627 if(aArg.match("--session="))
629 aPrevId = OUStringToOString(
630 aArg.subView(RTL_CONSTASCII_LENGTH("--session=")),
631 osl_getThreadTextEncoding());
632 break;
636 SAL_INFO("vcl.sm.debug", " previous ID = " << aPrevId);
637 return aPrevId;
640 void ICEConnectionObserver::activate()
642 SAL_INFO("vcl.sm", "ICEConnectionObserver::activate");
645 * Default handlers call exit, we don't care that strongly if something
646 * happens to fail
648 m_origIOErrorHandler = IceSetIOErrorHandler( IgnoreIceIOErrors );
649 m_origErrorHandler = IceSetErrorHandler( IgnoreIceErrors );
650 IceAddConnectionWatch( ICEWatchProc, this );
653 void ICEConnectionObserver::deactivate()
655 SAL_INFO("vcl.sm", "ICEConnectionObserver::deactivate");
657 oslThread t;
659 osl::MutexGuard g(m_ICEMutex);
660 IceRemoveConnectionWatch( ICEWatchProc, this );
661 IceSetErrorHandler( m_origErrorHandler );
662 IceSetIOErrorHandler( m_origIOErrorHandler );
663 m_nConnections = 0;
664 t = m_ICEThread;
665 m_ICEThread = nullptr;
667 if (t)
669 SAL_INFO("vcl.sm.debug", " terminate");
670 terminate(t);
674 void ICEConnectionObserver::wakeup()
676 SAL_INFO("vcl.sm", "ICEConnectionObserver::wakeup");
678 char cChar = 'w';
679 OSL_VERIFY(write(m_nWakeupFiles[1], &cChar, 1) == 1);
682 void ICEConnectionObserver::terminate(oslThread iceThread)
684 SAL_INFO("vcl.sm", "ICEConnectionObserver::terminate");
686 osl_terminateThread(iceThread);
687 wakeup();
688 osl_joinWithThread(iceThread);
689 osl_destroyThread(iceThread);
690 close(m_nWakeupFiles[1]);
691 close(m_nWakeupFiles[0]);
694 void ICEConnectionWorker(void * data)
696 SAL_INFO("vcl.sm", "ICEConnectionWorker");
698 osl::Thread::setName("ICEConnectionWorker");
699 ICEConnectionObserver * pThis = static_cast< ICEConnectionObserver * >(
700 data);
701 for (;;)
703 oslThread t;
705 osl::MutexGuard g(pThis->m_ICEMutex);
706 if (pThis->m_ICEThread == nullptr || pThis->m_nConnections == 0)
708 break;
710 t = pThis->m_ICEThread;
712 if (!osl_scheduleThread(t))
714 break;
717 int nConnectionsBefore;
718 struct pollfd* pLocalFD;
720 osl::MutexGuard g(pThis->m_ICEMutex);
721 nConnectionsBefore = pThis->m_nConnections;
722 int nBytes = sizeof( struct pollfd )*(nConnectionsBefore+1);
723 pLocalFD = static_cast<struct pollfd*>(std::malloc( nBytes ));
724 memcpy( pLocalFD, pThis->m_pFilehandles, nBytes );
727 int nRet = poll( pLocalFD,nConnectionsBefore+1,-1 );
728 bool bWakeup = (pLocalFD[0].revents & POLLIN);
729 std::free( pLocalFD );
731 if( nRet < 1 )
732 continue;
734 // clear wakeup pipe
735 if( bWakeup )
737 char buf[4];
738 while( read( pThis->m_nWakeupFiles[0], buf, sizeof( buf ) ) > 0 )
740 SAL_INFO("vcl.sm.debug", " file handles active in wakeup: " << nRet);
741 if( nRet == 1 )
742 continue;
745 // check fd's after we obtained the lock
746 osl::MutexGuard g(pThis->m_ICEMutex);
747 if( pThis->m_nConnections > 0 && pThis->m_nConnections == nConnectionsBefore )
749 nRet = poll( pThis->m_pFilehandles+1, pThis->m_nConnections, 0 );
750 if( nRet > 0 )
752 SAL_INFO("vcl.sm.debug", " IceProcessMessages");
753 Bool bReply;
754 for( int i = 0; i < pThis->m_nConnections; i++ )
755 if( pThis->m_pFilehandles[i+1].revents & POLLIN )
756 IceProcessMessages( pThis->m_pConnections[i], nullptr, &bReply );
761 SAL_INFO("vcl.sm.debug", " shutting down ICE dispatch thread");
764 void ICEWatchProc(
765 IceConn ice_conn, IcePointer client_data, Bool opening,
766 SAL_UNUSED_PARAMETER IcePointer *)
768 SAL_INFO("vcl.sm", "ICEWatchProc");
770 // Note: This is a callback function for ICE; this implicitly means that a
771 // call into ICE lib is calling this, so the m_ICEMutex MUST already be
772 // locked by the caller.
773 ICEConnectionObserver * pThis = static_cast< ICEConnectionObserver * >(
774 client_data);
775 if( opening )
777 SAL_INFO("vcl.sm.debug", " opening");
778 int fd = IceConnectionNumber( ice_conn );
779 pThis->m_nConnections++;
780 pThis->m_pConnections = static_cast<IceConn*>(std::realloc( pThis->m_pConnections, sizeof( IceConn )*pThis->m_nConnections ));
781 pThis->m_pFilehandles = static_cast<struct pollfd*>(std::realloc( pThis->m_pFilehandles, sizeof( struct pollfd )*(pThis->m_nConnections+1) ));
782 pThis->m_pConnections[ pThis->m_nConnections-1 ] = ice_conn;
783 pThis->m_pFilehandles[ pThis->m_nConnections ].fd = fd;
784 pThis->m_pFilehandles[ pThis->m_nConnections ].events = POLLIN;
785 if( pThis->m_nConnections == 1 )
787 SAL_INFO("vcl.sm.debug", " First connection");
788 if (!pipe(pThis->m_nWakeupFiles))
790 int flags;
791 pThis->m_pFilehandles[0].fd = pThis->m_nWakeupFiles[0];
792 pThis->m_pFilehandles[0].events = POLLIN;
793 // set close-on-exec and nonblock descriptor flag.
794 if ((flags = fcntl(pThis->m_nWakeupFiles[0], F_GETFD)) != -1)
796 flags |= FD_CLOEXEC;
797 (void)fcntl(pThis->m_nWakeupFiles[0], F_SETFD, flags);
799 if ((flags = fcntl(pThis->m_nWakeupFiles[0], F_GETFL)) != -1)
801 flags |= O_NONBLOCK;
802 (void)fcntl(pThis->m_nWakeupFiles[0], F_SETFL, flags);
804 // set close-on-exec and nonblock descriptor flag.
805 if ((flags = fcntl(pThis->m_nWakeupFiles[1], F_GETFD)) != -1)
807 flags |= FD_CLOEXEC;
808 (void)fcntl(pThis->m_nWakeupFiles[1], F_SETFD, flags);
810 if ((flags = fcntl(pThis->m_nWakeupFiles[1], F_GETFL)) != -1)
812 flags |= O_NONBLOCK;
813 (void)fcntl(pThis->m_nWakeupFiles[1], F_SETFL, flags);
815 pThis->m_ICEThread = osl_createThread(
816 ICEConnectionWorker, pThis);
820 else // closing
822 SAL_INFO("vcl.sm.debug", " closing");
823 for( int i = 0; i < pThis->m_nConnections; i++ )
825 if( pThis->m_pConnections[i] == ice_conn )
827 if( i < pThis->m_nConnections-1 )
829 memmove( pThis->m_pConnections+i, pThis->m_pConnections+i+1, sizeof( IceConn )*(pThis->m_nConnections-i-1) );
830 memmove( pThis->m_pFilehandles+i+1, pThis->m_pFilehandles+i+2, sizeof( struct pollfd )*(pThis->m_nConnections-i-1) );
832 pThis->m_nConnections--;
833 pThis->m_pConnections = static_cast<IceConn*>(std::realloc( pThis->m_pConnections, sizeof( IceConn )*pThis->m_nConnections ));
834 pThis->m_pFilehandles = static_cast<struct pollfd*>(std::realloc( pThis->m_pFilehandles, sizeof( struct pollfd )*(pThis->m_nConnections+1) ));
835 break;
838 if( pThis->m_nConnections == 0 && pThis->m_ICEThread )
840 SAL_INFO("vcl.sm.debug", " terminating ICEThread");
841 oslThread t = pThis->m_ICEThread;
842 pThis->m_ICEThread = nullptr;
844 // must release the mutex here
845 pThis->m_ICEMutex.release();
847 pThis->terminate(t);
849 // acquire the mutex again, because the caller does not expect
850 // it to be released when calling into SM
851 pThis->m_ICEMutex.acquire();
855 SAL_INFO( "vcl.sm.debug", " ICE connection on " << IceConnectionNumber( ice_conn ) );
856 SAL_INFO( "vcl.sm.debug", " Display connection is " << ConnectionNumber( vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDisplay() ) );
859 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */