1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 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/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 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 . 18 */ 19 20 #include <osl/module.hxx> 21 22 #include <rtl/bootstrap.hxx> 23 #include <rtl/process.h> 24 #include <sal/log.hxx> 25 26 #include <salframe.hxx> 27 #include <salinst.hxx> 28 #include <config_vclplug.h> 29 #include <desktop/crashreport.hxx> 30 31 #ifndef _WIN32 32 #include <headless/svpinst.hxx> 33 #include <printerinfomanager.hxx> 34 #include <unx/desktops.hxx> 35 36 #include <unistd.h> 37 #else 38 #include <saldatabasic.hxx> 39 #include <o3tl/char16_t2wchar_t.hxx> 40 #include <Windows.h> 41 #endif 42 43 #include <cstdio> 44 45 #ifdef ANDROID 46 #include <android/androidinst.hxx> 47 #endif 48 49 #if USING_X11 50 #define DESKTOPDETECT 51 #endif 52 #if ENABLE_HEADLESS 53 #define HEADLESS_VCLPLUG 54 #endif 55 56 extern "C" { 57 typedef SalInstance*(*salFactoryProc)(); 58 } 59 60 namespace { 61 62 #ifndef DISABLE_DYNLOADING 63 oslModule pCloseModule = nullptr; 64 #endif 65 66 SalInstance* tryInstance( const OUString& rModuleBase, bool bForce = false ) 67 { 68 #ifdef DISABLE_DYNLOADING 69 (void)rModuleBase; 70 (void)bForce; 71 return create_SalInstance(); 72 #else // !DISABLE_DYNLOADING 73 #ifdef HEADLESS_VCLPLUG 74 if (rModuleBase == "svp") 75 return svp_create_SalInstance(); 76 #endif 77 78 SalInstance* pInst = nullptr; 79 OUString aUsedModuleBase(rModuleBase); 80 if (aUsedModuleBase == "kde5") 81 aUsedModuleBase = "kf5"; 82 OUString aModule( 83 #ifdef SAL_DLLPREFIX 84 SAL_DLLPREFIX 85 #endif 86 "vclplug_" + aUsedModuleBase + "lo" SAL_DLLEXTENSION ); 87 88 osl::Module aMod; 89 if (aMod.loadRelative(reinterpret_cast<oslGenericFunction>(&tryInstance), aModule, SAL_LOADMODULE_GLOBAL)) 90 { 91 salFactoryProc aProc = reinterpret_cast<salFactoryProc>(aMod.getFunctionSymbol("create_SalInstance")); 92 if (aProc) 93 { 94 pInst = aProc(); 95 SAL_INFO( 96 "vcl.plugadapt", 97 "sal plugin " << aModule << " produced instance " << pInst); 98 if (pInst) 99 { 100 pCloseModule = static_cast<oslModule>(aMod); 101 aMod.release(); 102 103 /* 104 * Recent GTK+ versions load their modules with RTLD_LOCAL, so we can 105 * not access the 'gnome_accessibility_module_shutdown' anymore. 106 * So make sure libgtk+ & co are still mapped into memory when 107 * atk-bridge's atexit handler gets called. 108 */ 109 if (aUsedModuleBase == "gtk4" || aUsedModuleBase == "gtk3" || 110 aUsedModuleBase == "gtk3_kde5" || aUsedModuleBase == "win") 111 { 112 pCloseModule = nullptr; 113 } 114 } 115 } 116 else 117 { 118 SAL_WARN( 119 "vcl.plugadapt", 120 "could not load symbol create_SalInstance from shared object " 121 << aModule); 122 } 123 } 124 else if (bForce) 125 { 126 SAL_WARN("vcl.plugadapt", "could not load shared object " << aModule); 127 } 128 else 129 { 130 SAL_INFO("vcl.plugadapt", "could not load shared object " << aModule); 131 } 132 133 // coverity[leaked_storage] - this is on purpose 134 return pInst; 135 #endif // !DISABLE_DYNLOADING 136 } 137 138 #ifdef DESKTOPDETECT 139 #ifndef DISABLE_DYNLOADING 140 extern "C" typedef DesktopType Fn_get_desktop_environment(); 141 #endif 142 143 DesktopType lcl_get_desktop_environment() 144 { 145 DesktopType ret = DESKTOP_UNKNOWN; 146 #ifdef DISABLE_DYNLOADING 147 ret = get_desktop_environment(); 148 #else 149 OUString aModule(DESKTOP_DETECTOR_DLL_NAME); 150 oslModule aMod = osl_loadModuleRelative( 151 reinterpret_cast< oslGenericFunction >( &tryInstance ), aModule.pData, 152 SAL_LOADMODULE_DEFAULT ); 153 if( aMod ) 154 { 155 Fn_get_desktop_environment * pSym 156 = reinterpret_cast<Fn_get_desktop_environment *>( 157 osl_getAsciiFunctionSymbol(aMod, "get_desktop_environment")); 158 if( pSym ) 159 ret = pSym(); 160 } 161 osl_unloadModule( aMod ); 162 #endif 163 return ret; 164 } 165 166 SalInstance* autodetect_plugin() 167 { 168 #ifdef DISABLE_DYNLOADING 169 return nullptr; 170 #else // !DISABLE_DYNLOADING 171 static const char* const pKDEFallbackList[] = 172 { 173 #if ENABLE_KF5 174 "kf5", 175 #endif 176 #if ENABLE_GTK3_KDE5 177 "gtk3_kde5", 178 #endif 179 "gtk3", "gen", nullptr 180 }; 181 182 static const char* const pStandardFallbackList[] = 183 { 184 "gtk3", "gen", nullptr 185 }; 186 187 #ifdef HEADLESS_VCLPLUG 188 static const char* const pHeadlessFallbackList[] = 189 { 190 "svp", nullptr 191 }; 192 #endif 193 194 SalInstance* pInst = nullptr; 195 DesktopType desktop = lcl_get_desktop_environment(); 196 const char * const * pList = pStandardFallbackList; 197 int nListEntry = 0; 198 199 #ifdef HEADLESS_VCLPLUG 200 // no server at all: dummy plugin 201 if ( desktop == DESKTOP_NONE ) 202 pList = pHeadlessFallbackList; 203 else 204 #endif 205 if ( desktop == DESKTOP_GNOME || 206 desktop == DESKTOP_UNITY || 207 desktop == DESKTOP_XFCE || 208 desktop == DESKTOP_MATE ) 209 pList = pStandardFallbackList; 210 else if (desktop == DESKTOP_PLASMA5 || desktop == DESKTOP_LXQT) 211 pList = pKDEFallbackList; 212 213 while( pList[nListEntry] && pInst == nullptr ) 214 { 215 OUString aTry( OUString::createFromAscii( pList[nListEntry] ) ); 216 pInst = tryInstance( aTry ); 217 SAL_INFO_IF( 218 pInst, "vcl.plugadapt", 219 "plugin autodetection: " << pList[nListEntry]); 220 nListEntry++; 221 } 222 return pInst; 223 #endif // !DISABLE_DYNLOADING 224 } 225 #endif // DESKTOPDETECT 226 227 #ifdef HEADLESS_VCLPLUG 228 // HACK to obtain Application::IsHeadlessModeEnabled early on, before 229 // Application::EnableHeadlessMode has potentially been called: 230 bool IsHeadlessModeRequested() 231 { 232 if (Application::IsHeadlessModeEnabled()) { 233 return true; 234 } 235 sal_uInt32 n = rtl_getAppCommandArgCount(); 236 for (sal_uInt32 i = 0; i < n; ++i) { 237 OUString arg; 238 rtl_getAppCommandArg(i, &arg.pData); 239 if ( arg == "--headless" || arg == "-headless" ) { 240 return true; 241 } 242 } 243 return false; 244 } 245 #endif 246 247 } // anonymous namespace 248 249 SalInstance *CreateSalInstance() 250 { 251 SalInstance *pInst = nullptr; 252 OUString aUsePlugin; 253 rtl::Bootstrap::get("SAL_USE_VCLPLUGIN", aUsePlugin); 254 SAL_INFO_IF(!aUsePlugin.isEmpty(), "vcl", "Requested VCL plugin: " << aUsePlugin); 255 #ifdef HEADLESS_VCLPLUG 256 if (Application::IsBitmapRendering() || (aUsePlugin.isEmpty() && IsHeadlessModeRequested())) 257 aUsePlugin = "svp"; 258 #endif 259 260 if (aUsePlugin == "svp") 261 { 262 Application::EnableBitmapRendering(); 263 #ifndef HEADLESS_VCLPLUG 264 aUsePlugin.clear(); 265 #endif 266 } 267 268 if( !aUsePlugin.isEmpty() ) 269 pInst = tryInstance( aUsePlugin, true ); 270 271 #ifdef DESKTOPDETECT 272 if( ! pInst ) 273 pInst = autodetect_plugin(); 274 #endif 275 276 // fallback, try everything 277 static const char* const pPlugin[] = { 278 #ifdef _WIN32 279 "win" 280 #else 281 #ifdef MACOSX 282 "osx" 283 #else 284 "gtk3", "kf5", "gen" 285 #endif 286 #endif 287 }; 288 289 for ( int i = 0; !pInst && i != SAL_N_ELEMENTS(pPlugin); ++i ) 290 pInst = tryInstance( OUString::createFromAscii( pPlugin[ i ] ) ); 291 292 if( ! pInst ) 293 { 294 std::fprintf( stderr, "no suitable windowing system found, exiting.\n" ); 295 _exit( 1 ); 296 } 297 298 // acquire SolarMutex 299 pInst->AcquireYieldMutex(); 300 301 return pInst; 302 } 303 304 void DestroySalInstance( SalInstance *pInst ) 305 { 306 // release SolarMutex 307 pInst->ReleaseYieldMutexAll(); 308 309 delete pInst; 310 #ifndef DISABLE_DYNLOADING 311 if( pCloseModule ) 312 osl_unloadModule( pCloseModule ); 313 #endif 314 } 315 316 void SalAbort( const OUString& rErrorText, bool bDumpCore ) 317 { 318 if (GetSalData()->m_pInstance) 319 GetSalData()->m_pInstance->BeforeAbort(rErrorText, bDumpCore); 320 321 #if defined _WIN32 322 (void) bDumpCore; 323 if( rErrorText.isEmpty() ) 324 { 325 // make sure crash reporter is triggered 326 RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr ); 327 FatalAppExitW( 0, L"Application Error" ); 328 } 329 else 330 { 331 CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write); 332 // make sure crash reporter is triggered 333 RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr ); 334 FatalAppExitW( 0, o3tl::toW(rErrorText.getStr()) ); 335 } 336 #else 337 #if defined ANDROID 338 OUString aError(rErrorText.isEmpty() ? "Unspecified application error" : rErrorText); 339 LOGE("SalAbort: '%s'", OUStringToOString(aError, osl_getThreadTextEncoding()).getStr()); 340 #else 341 if( rErrorText.isEmpty() ) 342 std::fprintf( stderr, "Unspecified Application Error\n" ); 343 else 344 { 345 CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write); 346 std::fprintf( stderr, "%s\n", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr() ); 347 } 348 #endif 349 if( bDumpCore ) 350 abort(); 351 else 352 _exit(1); 353 #endif 354 } 355 356 const OUString& SalGetDesktopEnvironment() 357 { 358 #ifdef _WIN32 359 static OUString aDesktopEnvironment( "Windows" ); 360 #elif defined(MACOSX) 361 static OUString aDesktopEnvironment( "MacOSX" ); 362 #elif defined(EMSCRIPTEN) 363 static OUString aDesktopEnvironment("WASM"); 364 #elif defined(ANDROID) 365 static OUString aDesktopEnvironment("android"); 366 #elif USING_X11 367 // Order to match desktops.hxx' DesktopType 368 static const char * const desktop_strings[] = { 369 "none", "unknown", "GNOME", "UNITY", 370 "XFCE", "MATE", "PLASMA5", "LXQT" }; 371 static OUString aDesktopEnvironment; 372 if( aDesktopEnvironment.isEmpty()) 373 { 374 aDesktopEnvironment = OUString::createFromAscii( 375 desktop_strings[lcl_get_desktop_environment()]); 376 } 377 #else 378 static OUString aDesktopEnvironment("unknown"); 379 #endif 380 return aDesktopEnvironment; 381 } 382 383 SalData::SalData() : 384 m_pInstance(nullptr), 385 m_pPIManager(nullptr) 386 { 387 } 388 389 SalData::~SalData() COVERITY_NOEXCEPT_FALSE 390 { 391 #if (defined UNX && !defined MACOSX) 392 psp::PrinterInfoManager::release(); 393 #endif 394 } 395 396 #ifdef _WIN32 397 bool HasAtHook() 398 { 399 BOOL bIsRunning = FALSE; 400 // pvParam must be BOOL 401 return SystemParametersInfoW(SPI_GETSCREENREADER, 0, &bIsRunning, 0) 402 && bIsRunning; 403 } 404 #endif 405 406 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 407
