Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / source / app / svmain.cxx
blobe032edcc710fdb3b0228e90829c9d3224b656960
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 <sal/config.h>
21 #include <sal/log.hxx>
23 #include <cassert>
25 #include <osl/file.hxx>
26 #include <osl/signal.h>
28 #include <desktop/exithelper.h>
30 #include <comphelper/processfactory.hxx>
31 #include <comphelper/asyncnotification.hxx>
32 #include <i18nlangtag/mslangid.hxx>
33 #include <unotools/syslocaleoptions.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/vclmain.hxx>
36 #include <vcl/wrkwin.hxx>
37 #include <vcl/cvtgrf.hxx>
38 #include <vcl/scheduler.hxx>
39 #include <vcl/image.hxx>
40 #include <vcl/ImageTree.hxx>
41 #include <vcl/settings.hxx>
42 #include <vcl/toolkit/unowrap.hxx>
43 #include <vcl/configsettings.hxx>
44 #include <vcl/lazydelete.hxx>
45 #include <vcl/embeddedfontshelper.hxx>
46 #include <vcl/debugevent.hxx>
47 #include <vcl/dialog.hxx>
48 #include <vcl/menu.hxx>
49 #include <vcl/virdev.hxx>
50 #include <vcl/print.hxx>
51 #include <scrwnd.hxx>
53 #ifdef _WIN32
54 #include <svsys.h>
55 #include <process.h>
56 #include <ole2.h>
57 #else
58 #include <stdlib.h>
59 #endif
61 #ifdef ANDROID
62 #include <cppuhelper/bootstrap.hxx>
63 #include <jni.h>
64 #endif
66 #include <impfontcache.hxx>
67 #include <salinst.hxx>
68 #include <svdata.hxx>
69 #include <vcl/svmain.hxx>
70 #include <dbggui.hxx>
71 #include <accmgr.hxx>
72 #include <PhysicalFontCollection.hxx>
73 #include <print.h>
74 #include <salsys.hxx>
75 #include <saltimer.hxx>
76 #include <displayconnectiondispatch.hxx>
78 #include <config_features.h>
79 #include <config_feature_opencl.h>
81 #include <osl/process.h>
82 #include <com/sun/star/lang/XComponent.hpp>
83 #include <com/sun/star/frame/Desktop.hpp>
85 #include <comphelper/lok.hxx>
86 #include <cppuhelper/implbase.hxx>
87 #include <uno/current_context.hxx>
89 #include <opencl/OpenCLZone.hxx>
90 #include <opengl/zone.hxx>
91 #include <opengl/watchdog.hxx>
93 #include <basegfx/utils/systemdependentdata.hxx>
94 #include <tools/diagnose_ex.h>
96 #if OSL_DEBUG_LEVEL > 0
97 #include <typeinfo>
98 #include <rtl/strbuf.hxx>
99 #endif
101 using namespace ::com::sun::star;
103 static bool g_bIsLeanException;
105 static oslSignalAction VCLExceptionSignal_impl( void* /*pData*/, oslSignalInfo* pInfo)
107 static volatile bool bIn = false;
109 // if we crash again, bail out immediately
110 if ( bIn || g_bIsLeanException)
111 return osl_Signal_ActCallNextHdl;
113 ExceptionCategory nVCLException = ExceptionCategory::NONE;
115 // UAE
116 if ( (pInfo->Signal == osl_Signal_AccessViolation) ||
117 (pInfo->Signal == osl_Signal_IntegerDivideByZero) ||
118 (pInfo->Signal == osl_Signal_FloatDivideByZero) ||
119 (pInfo->Signal == osl_Signal_DebugBreak) )
121 nVCLException = ExceptionCategory::System;
122 #if HAVE_FEATURE_OPENGL
123 if (OpenGLZone::isInZone())
124 OpenGLZone::hardDisable();
125 #endif
126 #if HAVE_FEATURE_OPENCL
127 if (OpenCLZone::isInZone())
129 OpenCLZone::hardDisable();
130 #ifdef _WIN32
131 if (OpenCLZone::isInInitialTest())
132 TerminateProcess(GetCurrentProcess(), EXITHELPER_NORMAL_RESTART);
133 #endif
135 #endif
138 // RC
139 if ((pInfo->Signal == osl_Signal_User) &&
140 (pInfo->UserSignal == OSL_SIGNAL_USER_RESOURCEFAILURE) )
141 nVCLException = ExceptionCategory::ResourceNotLoaded;
143 // DISPLAY-Unix
144 if ((pInfo->Signal == osl_Signal_User) &&
145 (pInfo->UserSignal == OSL_SIGNAL_USER_X11SUBSYSTEMERROR) )
146 nVCLException = ExceptionCategory::UserInterface;
148 if ( nVCLException != ExceptionCategory::NONE )
150 bIn = true;
152 vcl::SolarMutexTryAndBuyGuard aLock;
153 if( aLock.isAcquired())
155 // do not stop timer because otherwise the UAE-Box will not be painted as well
156 ImplSVData* pSVData = ImplGetSVData();
157 if ( pSVData->mpApp )
159 SystemWindowFlags nOldMode = Application::GetSystemWindowMode();
160 Application::SetSystemWindowMode( nOldMode & ~SystemWindowFlags::NOAUTOMODE );
161 pSVData->mpApp->Exception( nVCLException );
162 Application::SetSystemWindowMode( nOldMode );
165 bIn = false;
168 return osl_Signal_ActCallNextHdl;
172 int ImplSVMain()
174 // The 'real' SVMain()
175 ImplSVData* pSVData = ImplGetSVData();
177 SAL_WARN_IF( !pSVData->mpApp, "vcl", "no instance of class Application" );
179 int nReturn = EXIT_FAILURE;
181 const bool bWasInitVCL = IsVCLInit();
182 const bool bInit = bWasInitVCL || InitVCL();
183 int nRet = 0;
184 if (!bWasInitVCL && bInit && pSVData->mpDefInst->SVMainHook(&nRet))
185 return nRet;
187 if( bInit )
189 // call application main
190 pSVData->maAppData.mbInAppMain = true;
191 nReturn = pSVData->mpApp->Main();
192 pSVData->maAppData.mbInAppMain = false;
195 if( pSVData->mxDisplayConnection.is() )
197 pSVData->mxDisplayConnection->terminate();
198 pSVData->mxDisplayConnection.clear();
201 // This is a hack to work around the problem of the asynchronous nature
202 // of bridging accessibility through Java: on shutdown there might still
203 // be some events in the AWT EventQueue, which need the SolarMutex which
204 // - on the other hand - is destroyed in DeInitVCL(). So empty the queue
205 // here ..
206 if( pSVData->mxAccessBridge.is() )
209 SolarMutexReleaser aReleaser;
210 pSVData->mxAccessBridge->dispose();
212 pSVData->mxAccessBridge.clear();
215 #if HAVE_FEATURE_OPENGL
216 OpenGLWatchdogThread::stop();
217 #endif
218 DeInitVCL();
220 return nReturn;
223 int SVMain()
225 return ImplSVMain();
228 // This variable is set when no Application object has been instantiated
229 // before InitVCL is called
230 static Application * pOwnSvApp = nullptr;
232 // Exception handler. pExceptionHandler != NULL => VCL already inited
233 static oslSignalHandler pExceptionHandler = nullptr;
235 class DesktopEnvironmentContext: public cppu::WeakImplHelper< css::uno::XCurrentContext >
237 public:
238 explicit DesktopEnvironmentContext( const css::uno::Reference< css::uno::XCurrentContext > & ctx)
239 : m_xNextContext( ctx ) {}
241 // XCurrentContext
242 virtual css::uno::Any SAL_CALL getValueByName( const OUString& Name ) override;
244 private:
245 css::uno::Reference< css::uno::XCurrentContext > m_xNextContext;
248 uno::Any SAL_CALL DesktopEnvironmentContext::getValueByName( const OUString& Name)
250 uno::Any retVal;
252 if ( Name == "system.desktop-environment" )
254 retVal <<= Application::GetDesktopEnvironment();
256 else if( m_xNextContext.is() )
258 // Call next context in chain if found
259 retVal = m_xNextContext->getValueByName( Name );
261 return retVal;
264 bool IsVCLInit()
266 ImplSVData* pSVData = ImplGetSVData();
267 return pExceptionHandler != nullptr &&
268 pSVData->mpApp != nullptr &&
269 pSVData->mpDefInst != nullptr;
272 #ifdef DBG_UTIL
273 namespace vclmain
275 bool isAlive()
277 return ImplGetSVData()->mpDefInst;
280 #endif
283 bool InitVCL()
285 if (IsVCLInit())
287 SAL_INFO("vcl.app", "Double initialization of vcl");
288 return true;
291 if( pExceptionHandler != nullptr )
292 return false;
294 EmbeddedFontsHelper::clearTemporaryFontFiles();
296 if( !ImplGetSVData()->mpApp )
298 pOwnSvApp = new Application();
301 ImplSVData* pSVData = ImplGetSVData();
303 // remember Main-Thread-Id
304 pSVData->mnMainThreadId = ::osl::Thread::getCurrentIdentifier();
306 // Initialize Sal
307 pSVData->mpDefInst = CreateSalInstance();
308 if ( !pSVData->mpDefInst )
309 return false;
311 // Desktop Environment context (to be able to get value of "system.desktop-environment" as soon as possible)
312 css::uno::setCurrentContext(
313 new DesktopEnvironmentContext( css::uno::getCurrentContext() ) );
315 // Initialize application instance (should be done after initialization of VCL SAL part)
316 if (pSVData->mpApp)
318 // call init to initialize application class
319 // soffice/sfx implementation creates the global service manager
320 pSVData->mpApp->Init();
325 //Now that uno has been bootstrapped we can ask the config what the UI language is so that we can
326 //force that in as $LANGUAGE. That way we can get gtk to render widgets RTL
327 //if we have a RTL UI in an otherwise LTR locale and get gettext using externals (e.g. python)
328 //to match their translations to our preferred UI language
329 OUString aLocaleString(SvtSysLocaleOptions().GetRealUILanguageTag().getGlibcLocaleString(".UTF-8"));
330 if (!aLocaleString.isEmpty())
332 MsLangId::getSystemUILanguage(); //call this now to pin what the system UI really was
333 OUString envVar("LANGUAGE");
334 osl_setEnvironment(envVar.pData, aLocaleString.pData);
337 catch (const uno::Exception &)
339 TOOLS_INFO_EXCEPTION("vcl.app", "Unable to get ui language:");
342 pSVData->mpDefInst->AfterAppInit();
344 // Fetch AppFileName and make it absolute before the workdir changes...
345 OUString aExeFileName;
346 osl_getExecutableFile( &aExeFileName.pData );
348 // convert path to native file format
349 OUString aNativeFileName;
350 osl::FileBase::getSystemPathFromFileURL( aExeFileName, aNativeFileName );
351 pSVData->maAppData.mxAppFileName = aNativeFileName;
353 // Initialize global data
354 pSVData->maGDIData.mxScreenFontList.reset(new PhysicalFontCollection);
355 pSVData->maGDIData.mxScreenFontCache.reset(new ImplFontCache);
356 pSVData->maGDIData.mpGrfConverter = new GraphicConverter;
358 g_bIsLeanException = getenv("LO_LEAN_EXCEPTION") != nullptr;
359 // Set exception handler
360 pExceptionHandler = osl_addSignalHandler(VCLExceptionSignal_impl, nullptr);
362 #ifndef NDEBUG
363 DbgGUIInitSolarMutexCheck();
364 #endif
366 #if OSL_DEBUG_LEVEL > 0
367 DebugEventInjector::getCreate();
368 #endif
370 #ifndef _WIN32
371 // Clear startup notification details for child processes
372 // See https://bugs.freedesktop.org/show_bug.cgi?id=11375 for discussion
373 unsetenv("DESKTOP_STARTUP_ID");
374 #endif
376 return true;
379 namespace
382 /** Serves for destroying the VCL UNO wrapper as late as possible. This avoids
383 crash at exit in some special cases when a11y is enabled (e.g., when
384 a bundled extension is registered/deregistered during startup, forcing exit
385 while the app is still in splash screen.)
387 class VCLUnoWrapperDeleter : public cppu::WeakImplHelper<css::lang::XEventListener>
389 virtual void SAL_CALL disposing(lang::EventObject const& rSource) override;
392 void
393 VCLUnoWrapperDeleter::disposing(lang::EventObject const& /* rSource */)
395 ImplSVData* const pSVData = ImplGetSVData();
396 if (pSVData && pSVData->mpUnoWrapper)
398 pSVData->mpUnoWrapper->Destroy();
399 pSVData->mpUnoWrapper = nullptr;
405 void DeInitVCL()
407 //rhbz#1444437, when using LibreOffice like a library you can't realistically
408 //tear everything down and recreate them on the next call, there's too many
409 //(c++) singletons that point to stuff that gets deleted during shutdown
410 //which won't be recreated on restart.
411 if (comphelper::LibreOfficeKit::isActive())
412 return;
415 SolarMutexReleaser r; // unblock threads blocked on that so we can join
416 ::comphelper::JoinAsyncEventNotifiers();
418 ImplSVData* pSVData = ImplGetSVData();
420 // lp#1560328: clear cache before disposing rest of VCL
421 if(pSVData->mpBlendFrameCache)
422 pSVData->mpBlendFrameCache->m_aLastResult.Clear();
423 pSVData->mbDeInit = true;
425 vcl::DeleteOnDeinitBase::ImplDeleteOnDeInit();
427 #if OSL_DEBUG_LEVEL > 0
428 OStringBuffer aBuf( 256 );
429 aBuf.append( "DeInitVCL: some top Windows are still alive\n" );
430 long nTopWindowCount = Application::GetTopWindowCount();
431 long nBadTopWindows = nTopWindowCount;
432 for( long i = 0; i < nTopWindowCount; i++ )
434 vcl::Window* pWin = Application::GetTopWindow( i );
435 // default window will be destroyed further down
436 // but may still be useful during deinit up to that point
437 if( pWin == pSVData->mpDefaultWin )
438 nBadTopWindows--;
439 else
441 aBuf.append( "text = \"" );
442 aBuf.append( OUStringToOString( pWin->GetText(), osl_getThreadTextEncoding() ) );
443 aBuf.append( "\" type = \"" );
444 aBuf.append( typeid(*pWin).name() );
445 aBuf.append( "\", ptr = 0x" );
446 aBuf.append( sal_Int64( pWin ), 16 );
447 aBuf.append( "\n" );
450 SAL_WARN_IF( nBadTopWindows!=0, "vcl", aBuf.getStr() );
451 #endif
453 ImageTree::get().shutdown();
455 osl_removeSignalHandler( pExceptionHandler);
456 pExceptionHandler = nullptr;
458 // free global data
459 if (pSVData->maGDIData.mpGrfConverter)
461 delete pSVData->maGDIData.mpGrfConverter;
462 pSVData->maGDIData.mpGrfConverter = nullptr;
465 pSVData->mpSettingsConfigItem.reset();
467 // prevent unnecessary painting during Scheduler shutdown
468 // as this processes all pending events in debug builds.
469 ImplGetSystemDependentDataManager().flushAll();
471 Scheduler::ImplDeInitScheduler();
473 pSVData->maWinData.maMsgBoxImgList.clear();
474 pSVData->maCtrlData.maCheckImgList.clear();
475 pSVData->maCtrlData.maRadioImgList.clear();
476 pSVData->maCtrlData.mpDisclosurePlus.reset();
477 pSVData->maCtrlData.mpDisclosureMinus.reset();
478 pSVData->mpDefaultWin.disposeAndClear();
480 #ifndef NDEBUG
481 DbgGUIDeInitSolarMutexCheck();
482 #endif
484 if ( pSVData->mpUnoWrapper )
488 uno::Reference<frame::XDesktop2> const xDesktop = frame::Desktop::create(
489 comphelper::getProcessComponentContext() );
490 xDesktop->addEventListener(new VCLUnoWrapperDeleter);
492 catch (uno::Exception const&)
494 // ignore
498 if( pSVData->mpApp || pSVData->maDeInitHook.IsSet() )
500 SolarMutexReleaser aReleaser;
501 // call deinit to deinitialize application class
502 // soffice/sfx implementation disposes the global service manager
503 // Warning: After this call you can't call uno services
504 if( pSVData->mpApp )
506 pSVData->mpApp->DeInit();
508 if( pSVData->maDeInitHook.IsSet() )
510 pSVData->maDeInitHook.Call(nullptr);
514 if ( pSVData->maAppData.mpSettings )
516 if ( pSVData->maAppData.mpCfgListener )
518 pSVData->maAppData.mpSettings->GetSysLocale().GetOptions().RemoveListener( pSVData->maAppData.mpCfgListener );
519 delete pSVData->maAppData.mpCfgListener;
522 pSVData->maAppData.mpSettings.reset();
524 if (pSVData->maAppData.mpAccelMgr)
526 delete pSVData->maAppData.mpAccelMgr;
527 pSVData->maAppData.mpAccelMgr = nullptr;
529 pSVData->maAppData.maKeyListeners.clear();
530 pSVData->mpBlendFrameCache.reset();
532 ImplDeletePrnQueueList();
534 // destroy all Sal interfaces before destroying the instance
535 // and thereby unloading the plugin
536 pSVData->mpSalSystem.reset();
537 assert( !pSVData->maSchedCtx.mpSalTimer );
538 delete pSVData->maSchedCtx.mpSalTimer;
539 pSVData->maSchedCtx.mpSalTimer = nullptr;
541 pSVData->mpDefaultWin = nullptr;
542 pSVData->mpIntroWindow = nullptr;
543 pSVData->maAppData.mpActivePopupMenu = nullptr;
544 pSVData->maAppData.mpWheelWindow = nullptr;
545 pSVData->maGDIData.mpFirstWinGraphics = nullptr;
546 pSVData->maGDIData.mpLastWinGraphics = nullptr;
547 pSVData->maGDIData.mpFirstVirGraphics = nullptr;
548 pSVData->maGDIData.mpLastVirGraphics = nullptr;
549 pSVData->maGDIData.mpFirstPrnGraphics = nullptr;
550 pSVData->maGDIData.mpLastPrnGraphics = nullptr;
551 pSVData->maGDIData.mpFirstVirDev = nullptr;
552 pSVData->maGDIData.mpFirstPrinter = nullptr;
553 pSVData->maWinData.mpFirstFrame = nullptr;
554 pSVData->maWinData.mpAppWin = nullptr;
555 pSVData->maWinData.mpActiveApplicationFrame = nullptr;
556 pSVData->maWinData.mpCaptureWin = nullptr;
557 pSVData->maWinData.mpLastDeacWin = nullptr;
558 pSVData->maWinData.mpFirstFloat = nullptr;
559 pSVData->maWinData.mpExecuteDialogs.clear();
560 pSVData->maWinData.mpExtTextInputWin = nullptr;
561 pSVData->maWinData.mpTrackWin = nullptr;
562 pSVData->maWinData.mpAutoScrollWin = nullptr;
563 pSVData->maWinData.mpLastWheelWindow = nullptr;
565 pSVData->maGDIData.mxScreenFontList.reset();
566 pSVData->maGDIData.mxScreenFontCache.reset();
567 pSVData->maGDIData.maScaleCache.remove_if([](const o3tl::lru_map<SalBitmap*, BitmapEx>::key_value_pair_t&)
568 { return true; });
570 pSVData->maGDIData.maThemeDrawCommandsCache.clear();
571 pSVData->maGDIData.maThemeImageCache.clear();
573 // Deinit Sal
574 if (pSVData->mpDefInst)
576 DestroySalInstance( pSVData->mpDefInst );
577 pSVData->mpDefInst = nullptr;
580 if( pOwnSvApp )
582 delete pOwnSvApp;
583 pOwnSvApp = nullptr;
586 EmbeddedFontsHelper::clearTemporaryFontFiles();
589 // only one call is allowed
590 struct WorkerThreadData
592 oslWorkerFunction const pWorker;
593 void * const pThreadData;
594 WorkerThreadData( oslWorkerFunction pWorker_, void * pThreadData_ )
595 : pWorker( pWorker_ )
596 , pThreadData( pThreadData_ )
601 #ifdef _WIN32
602 static HANDLE hThreadID = nullptr;
603 static unsigned __stdcall threadmain( void *pArgs )
605 OleInitialize( nullptr );
606 static_cast<WorkerThreadData*>(pArgs)->pWorker( static_cast<WorkerThreadData*>(pArgs)->pThreadData );
607 delete static_cast<WorkerThreadData*>(pArgs);
608 OleUninitialize();
609 hThreadID = nullptr;
610 return 0;
612 #else
613 static oslThread hThreadID = nullptr;
614 extern "C"
616 static void MainWorkerFunction( void* pArgs )
618 static_cast<WorkerThreadData*>(pArgs)->pWorker( static_cast<WorkerThreadData*>(pArgs)->pThreadData );
619 delete static_cast<WorkerThreadData*>(pArgs);
620 hThreadID = nullptr;
622 } // extern "C"
623 #endif
625 void CreateMainLoopThread( oslWorkerFunction pWorker, void * pThreadData )
627 #ifdef _WIN32
628 // sal thread always call CoInitializeEx, so a system dependent implementation is necessary
630 unsigned uThreadID;
631 hThreadID = reinterpret_cast<HANDLE>(_beginthreadex(
632 nullptr, // no security handle
633 0, // stacksize 0 means default
634 threadmain, // thread worker function
635 new WorkerThreadData( pWorker, pThreadData ), // arguments for worker function
636 0, // 0 means: create immediately otherwise use CREATE_SUSPENDED
637 &uThreadID )); // thread id to fill
638 #else
639 hThreadID = osl_createThread( MainWorkerFunction, new WorkerThreadData( pWorker, pThreadData ) );
640 #endif
643 void JoinMainLoopThread()
645 if( hThreadID )
647 #ifdef _WIN32
648 WaitForSingleObject(hThreadID, INFINITE);
649 #else
650 osl_joinWithThread(hThreadID);
651 osl_destroyThread( hThreadID );
652 #endif
656 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */