diff options
Diffstat (limited to 'vcl/source/app/salplug.cxx')
-rw-r--r-- | vcl/source/app/salplug.cxx | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/vcl/source/app/salplug.cxx b/vcl/source/app/salplug.cxx new file mode 100644 index 000000000..378187dbf --- /dev/null +++ b/vcl/source/app/salplug.cxx @@ -0,0 +1,450 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +// Th current high-level preprocessor structure is: +// +// if !HAVE_FEATURE_UI +// => STATIC_SAL_INSTANCE +// else +// ? !STATIC_SAL_INSTANCE +// ? UNIX_DESKTOP_DETECT +// endif +// +// ENABLE_HEADLESS just signifies the use of the SVP plugin! + +#include <config_features.h> +#include <config_vclplug.h> + +#include <cstdio> +#include <desktop/crashreport.hxx> +#include <rtl/bootstrap.hxx> +#include <rtl/process.h> +#include <salinst.hxx> +#include <sal/log.hxx> +#include <svdata.hxx> +#include <vcl/svapp.hxx> + +#if USING_X11 +#define UNIX_DESKTOP_DETECT 1 +#include <unx/desktops.hxx> +#else +#define UNIX_DESKTOP_DETECT 0 +#endif + +#if defined(DISABLE_DYNLOADING) || !HAVE_FEATURE_UI +#define STATIC_SAL_INSTANCE 1 +extern "C" SalInstance* create_SalInstance(); +#else +#define STATIC_SAL_INSTANCE 0 +#include <osl/module.hxx> +#endif + +#if defined(iOS) +#include <premac.h> +#include <UIKit/UIKit.h> +#include <postmac.h> + +#elif defined(ANDROID) +#include <android/androidinst.hxx> +#endif + +#if defined(_WIN32) +#include <o3tl/char16_t2wchar_t.hxx> +#include <salframe.hxx> +#include <Windows.h> +#else +#include <unistd.h> +#endif + +#if ENABLE_HEADLESS +#include <headless/svpdata.hxx> +#include <headless/svpinst.hxx> +#endif + +namespace { + +#if ENABLE_HEADLESS +SalInstance* svp_create_SalInstance() +{ + SvpSalInstance* pInstance = new SvpSalInstance(std::make_unique<SvpSalYieldMutex>()); + new SvpSalData(); + return pInstance; +} +#endif + +#if HAVE_FEATURE_UI + +#if !STATIC_SAL_INSTANCE +oslModule pCloseModule = nullptr; + +extern "C" typedef SalInstance* (*salFactoryProc)(); + +SalInstance* tryInstance( const OUString& rModuleBase, bool bForce = false ) +{ +#if ENABLE_HEADLESS + if (rModuleBase == "svp") + return svp_create_SalInstance(); +#endif + + SalInstance* pInst = nullptr; + OUString aUsedModuleBase(rModuleBase); + if (aUsedModuleBase == "kde5") + aUsedModuleBase = "kf5"; + OUString aModule( +#ifdef SAL_DLLPREFIX + SAL_DLLPREFIX +#endif + "vclplug_" + aUsedModuleBase + "lo" SAL_DLLEXTENSION ); + + osl::Module aMod; + if (aMod.loadRelative(reinterpret_cast<oslGenericFunction>(&tryInstance), aModule, SAL_LOADMODULE_GLOBAL)) + { + salFactoryProc aProc = reinterpret_cast<salFactoryProc>(aMod.getFunctionSymbol("create_SalInstance")); + if (aProc) + { + pInst = aProc(); + SAL_INFO( + "vcl.plugadapt", + "sal plugin " << aModule << " produced instance " << pInst); + if (pInst) + { + pCloseModule = static_cast<oslModule>(aMod); + aMod.release(); + + /* + * Recent GTK+ versions load their modules with RTLD_LOCAL, so we can + * not access the 'gnome_accessibility_module_shutdown' anymore. + * So make sure libgtk+ & co are still mapped into memory when + * atk-bridge's atexit handler gets called. + */ + if (aUsedModuleBase == "gtk4" || aUsedModuleBase == "gtk3" || + aUsedModuleBase == "gtk3_kde5" || aUsedModuleBase == "kf5" || + aUsedModuleBase == "qt5" || aUsedModuleBase == "qt6" || + aUsedModuleBase == "win") + { + pCloseModule = nullptr; + } + } + } + else + { + SAL_WARN( + "vcl.plugadapt", + "could not load symbol create_SalInstance from shared object " + << aModule); + } + } + else if (bForce) + { + SAL_WARN("vcl.plugadapt", "could not load shared object " << aModule); + } + else + { + SAL_INFO("vcl.plugadapt", "could not load shared object " << aModule); + } + + // coverity[leaked_storage] - this is on purpose + return pInst; +} +#endif // !STATIC_SAL_INSTANCE + +#if UNIX_DESKTOP_DETECT +#ifndef DISABLE_DYNLOADING +extern "C" typedef DesktopType Fn_get_desktop_environment(); +#else +extern "C" DesktopType get_desktop_environment(); +#endif + +DesktopType lcl_get_desktop_environment() +{ + DesktopType ret = DESKTOP_UNKNOWN; +#ifdef DISABLE_DYNLOADING + ret = get_desktop_environment(); +#else + OUString aModule(DESKTOP_DETECTOR_DLL_NAME); + oslModule aMod = osl_loadModuleRelative( + reinterpret_cast< oslGenericFunction >( &tryInstance ), aModule.pData, + SAL_LOADMODULE_DEFAULT ); + if( aMod ) + { + Fn_get_desktop_environment * pSym + = reinterpret_cast<Fn_get_desktop_environment *>( + osl_getAsciiFunctionSymbol(aMod, "get_desktop_environment")); + if( pSym ) + ret = pSym(); + } + osl_unloadModule( aMod ); +#endif + return ret; +} + +#if !STATIC_SAL_INSTANCE +const char* const* autodetect_plugin_list() +{ + static const char* const pKDEFallbackList[] = + { +#if ENABLE_KF5 + "kf5", +#endif +#if ENABLE_GTK3_KDE5 + "gtk3_kde5", +#endif +#if ENABLE_GTK3 + "gtk3", +#endif +#if ENABLE_GEN + "gen", +#endif + nullptr + }; + + static const char* const pStandardFallbackList[] = + { +#if ENABLE_GTK3 + "gtk3", +#endif +#if ENABLE_GEN + "gen", +#endif + nullptr + }; + +#if ENABLE_HEADLESS + static const char* const pHeadlessFallbackList[] = + { + "svp", + nullptr + }; +#endif + + DesktopType desktop = lcl_get_desktop_environment(); + const char * const * pList = pStandardFallbackList; + +#if ENABLE_HEADLESS + // no server at all: dummy plugin + if ( desktop == DESKTOP_NONE ) + pList = pHeadlessFallbackList; + else +#endif + if ( desktop == DESKTOP_GNOME || + desktop == DESKTOP_UNITY || + desktop == DESKTOP_XFCE || + desktop == DESKTOP_MATE ) + pList = pStandardFallbackList; + else if (desktop == DESKTOP_PLASMA5 || desktop == DESKTOP_LXQT) + pList = pKDEFallbackList; + + return pList; +} +#endif // !STATIC_SAL_INSTANCE +#endif // UNIX_DESKTOP_DETECT + +#endif // HAVE_FEATURE_UI + +// HACK to obtain Application::IsHeadlessModeEnabled early on, before +// Application::EnableHeadlessMode has potentially been called: +bool IsHeadlessModeRequested() +{ + if (Application::IsHeadlessModeEnabled()) { + return true; + } + sal_uInt32 n = rtl_getAppCommandArgCount(); + for (sal_uInt32 i = 0; i < n; ++i) { + OUString arg; + rtl_getAppCommandArg(i, &arg.pData); + if ( arg == "--headless" || arg == "-headless" ) { + return true; + } + } + return false; +} + +} // anonymous namespace + +SalInstance *CreateSalInstance() +{ + OUString aUsePlugin; + rtl::Bootstrap::get("SAL_USE_VCLPLUGIN", aUsePlugin); + SAL_INFO_IF(!aUsePlugin.isEmpty(), "vcl.plugadapt", "Requested VCL plugin: " << aUsePlugin); + + if (Application::IsBitmapRendering() || (aUsePlugin.isEmpty() && IsHeadlessModeRequested())) + aUsePlugin = "svp"; + + if (aUsePlugin == "svp") + { + Application::EnableBitmapRendering(); +#if ENABLE_HEADLESS + return svp_create_SalInstance(); +#else + aUsePlugin.clear(); +#endif + } + +#if STATIC_SAL_INSTANCE + return create_SalInstance(); + +#else // !STATIC_SAL_INSTANCE + SalInstance *pInst = nullptr; + + if( !aUsePlugin.isEmpty() ) + pInst = tryInstance( aUsePlugin, true ); + +#if UNIX_DESKTOP_DETECT + const char* const* pPluginList = pInst ? nullptr : autodetect_plugin_list(); + for (int i = 0; !pInst && pPluginList[i]; ++i) + { + pInst = tryInstance(OUString::createFromAscii(pPluginList[i])); + SAL_INFO_IF(pInst, "vcl.plugadapt", "plugin autodetection: " << pPluginList[i]); + } +#endif + + // fallback, try everything + static const char* const pPlugin[] = { +#ifdef _WIN32 + "win", +#elif defined(MACOSX) + "osx", +#else // !_WIN32 && !MACOSX +#if ENABLE_GTK3 + "gtk3", +#endif +#if ENABLE_KF5 + "kf5", +#endif +#if ENABLE_GTK3_KDE5 + "gtk3_kde5", +#endif +#if ENABLE_GEN + "gen", +#endif +#if ENABLE_QT5 + "qt5", +#endif +#if ENABLE_QT6 + "qt6", +#endif +#endif // !_WIN32 && !MACOSX + nullptr + }; + + for (int i = 0; !pInst && pPlugin[i]; ++i) + pInst = tryInstance( OUString::createFromAscii( pPlugin[ i ] ) ); + + if( ! pInst ) + { + std::fprintf( stderr, "no suitable windowing system found, exiting.\n" ); + _exit( 1 ); + } + + return pInst; +#endif // !STATIC_SAL_INSTANCE +} + +void DestroySalInstance( SalInstance *pInst ) +{ + delete pInst; +#if !STATIC_SAL_INSTANCE + if( pCloseModule ) + osl_unloadModule( pCloseModule ); +#endif +} + +void SalAbort( const OUString& rErrorText, bool bDumpCore ) +{ + if (GetSalInstance()) + GetSalInstance()->BeforeAbort(rErrorText, bDumpCore); + +#if defined _WIN32 + if( rErrorText.isEmpty() ) + { + // make sure crash reporter is triggered + RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr ); + FatalAppExitW( 0, L"Application Error" ); + } + else + { + CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write); + // make sure crash reporter is triggered + RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr ); + FatalAppExitW( 0, o3tl::toW(rErrorText.getStr()) ); + } +#else // !_WIN32 +#if defined ANDROID + OUString aError(rErrorText.isEmpty() ? "Unspecified application error" : rErrorText); + LOGE("SalAbort: '%s'", OUStringToOString(aError, osl_getThreadTextEncoding()).getStr()); +#elif defined(iOS) + NSLog(@"SalAbort: %s", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr()); +#else + if( rErrorText.isEmpty() ) + std::fprintf( stderr, "Unspecified Application Error\n" ); + else + { + CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write); + std::fprintf( stderr, "%s\n", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr() ); + } +#endif + if( bDumpCore ) + abort(); + else + _exit(1); +#endif // !_WIN32 +} + +const OUString& SalGetDesktopEnvironment() +{ +#if !HAVE_FEATURE_UI + static OUString aDesktopEnvironment("headless"); +#elif defined(_WIN32) + static OUString aDesktopEnvironment( "Windows" ); +#elif defined(MACOSX) + static OUString aDesktopEnvironment( "MacOSX" ); +#elif defined(EMSCRIPTEN) + static OUString aDesktopEnvironment("WASM"); +#elif defined(ANDROID) + static OUString aDesktopEnvironment("android"); +#elif defined(iOS) + static OUString aDesktopEnvironment("iOS"); +#elif UNIX_DESKTOP_DETECT + // Order to match desktops.hxx' DesktopType + static const char * const desktop_strings[] = { + "none", "unknown", "GNOME", "UNITY", + "XFCE", "MATE", "PLASMA5", "LXQT" }; + static OUString aDesktopEnvironment; + if( aDesktopEnvironment.isEmpty()) + { + aDesktopEnvironment = OUString::createFromAscii( + desktop_strings[lcl_get_desktop_environment()]); + } +#else + static OUString aDesktopEnvironment("unknown"); +#endif + return aDesktopEnvironment; +} + +#ifdef _WIN32 +bool HasAtHook() +{ + BOOL bIsRunning = FALSE; + // pvParam must be BOOL + return SystemParametersInfoW(SPI_GETSCREENREADER, 0, &bIsRunning, 0) + && bIsRunning; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |