bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / app / salplug.cxx
blobe51375b94ae53db8826cf05b8fc87fb71ec134e4
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 // Th current high-level preprocessor structure is:
22 // if !HAVE_FEATURE_UI
23 // => STATIC_SAL_INSTANCE
24 // else
25 // ? !STATIC_SAL_INSTANCE
26 // ? UNIX_DESKTOP_DETECT
27 // endif
29 // ENABLE_HEADLESS just signifies the use of the SVP plugin!
31 #include <config_features.h>
32 #include <config_vclplug.h>
34 #include <cstdio>
35 #include <desktop/crashreport.hxx>
36 #include <rtl/bootstrap.hxx>
37 #include <rtl/process.h>
38 #include <salinst.hxx>
39 #include <sal/log.hxx>
40 #include <svdata.hxx>
41 #include <vcl/svapp.hxx>
43 #if HAVE_FEATURE_UI
44 #if USING_X11
45 #define UNIX_DESKTOP_DETECT 1
46 #include <unx/desktops.hxx>
47 #else
48 #define UNIX_DESKTOP_DETECT 0
49 #endif
50 #endif
52 #if defined(DISABLE_DYNLOADING) || !HAVE_FEATURE_UI
53 #define STATIC_SAL_INSTANCE 1
54 extern "C" SalInstance* create_SalInstance();
55 #else
56 #define STATIC_SAL_INSTANCE 0
57 #include <osl/module.hxx>
58 #endif
60 #if defined(iOS)
61 #include <premac.h>
62 #include <UIKit/UIKit.h>
63 #include <postmac.h>
65 #elif defined(ANDROID)
66 #include <android/androidinst.hxx>
67 #endif
69 #if defined(_WIN32)
70 #include <o3tl/char16_t2wchar_t.hxx>
71 #include <salframe.hxx>
72 #include <Windows.h>
73 #else
74 #include <unistd.h>
75 #endif
77 #if ENABLE_HEADLESS
78 #include <headless/svpdata.hxx>
79 #include <headless/svpinst.hxx>
80 #endif
82 namespace {
84 #if ENABLE_HEADLESS
85 SalInstance* svp_create_SalInstance()
87 SvpSalInstance* pInstance = new SvpSalInstance(std::make_unique<SvpSalYieldMutex>());
88 new SvpSalData();
89 return pInstance;
91 #endif
93 #if HAVE_FEATURE_UI
95 #if !STATIC_SAL_INSTANCE
96 oslModule pCloseModule = nullptr;
98 extern "C" typedef SalInstance* (*salFactoryProc)();
100 SalInstance* tryInstance( const OUString& rModuleBase, bool bForce = false )
102 #if ENABLE_HEADLESS
103 if (rModuleBase == "svp")
104 return svp_create_SalInstance();
105 #endif
107 SalInstance* pInst = nullptr;
108 OUString aUsedModuleBase(rModuleBase);
109 if (aUsedModuleBase == "kde5")
110 aUsedModuleBase = "kf5";
111 OUString aModule(
112 #ifdef SAL_DLLPREFIX
113 SAL_DLLPREFIX
114 #endif
115 "vclplug_" + aUsedModuleBase + "lo" SAL_DLLEXTENSION );
117 osl::Module aMod;
118 if (aMod.loadRelative(reinterpret_cast<oslGenericFunction>(&tryInstance), aModule, SAL_LOADMODULE_GLOBAL))
120 salFactoryProc aProc = reinterpret_cast<salFactoryProc>(aMod.getFunctionSymbol("create_SalInstance"));
121 if (aProc)
123 pInst = aProc();
124 SAL_INFO(
125 "vcl.plugadapt",
126 "sal plugin " << aModule << " produced instance " << pInst);
127 if (pInst)
129 pCloseModule = static_cast<oslModule>(aMod);
130 aMod.release();
133 * Recent GTK+ versions load their modules with RTLD_LOCAL, so we can
134 * not access the 'gnome_accessibility_module_shutdown' anymore.
135 * So make sure libgtk+ & co are still mapped into memory when
136 * atk-bridge's atexit handler gets called.
138 if (aUsedModuleBase == "gtk4" || aUsedModuleBase == "gtk3" ||
139 aUsedModuleBase == "gtk3_kde5" || aUsedModuleBase == "kf5" ||
140 aUsedModuleBase == "qt5" || aUsedModuleBase == "qt6" ||
141 aUsedModuleBase == "win")
143 pCloseModule = nullptr;
147 else
149 SAL_WARN(
150 "vcl.plugadapt",
151 "could not load symbol create_SalInstance from shared object "
152 << aModule);
155 else if (bForce)
157 SAL_WARN("vcl.plugadapt", "could not load shared object " << aModule);
159 else
161 SAL_INFO("vcl.plugadapt", "could not load shared object " << aModule);
164 // coverity[leaked_storage] - this is on purpose
165 return pInst;
167 #endif // !STATIC_SAL_INSTANCE
169 #if UNIX_DESKTOP_DETECT
170 #ifndef DISABLE_DYNLOADING
171 extern "C" typedef DesktopType Fn_get_desktop_environment();
172 #else
173 extern "C" DesktopType get_desktop_environment();
174 #endif
176 DesktopType lcl_get_desktop_environment()
178 DesktopType ret = DESKTOP_UNKNOWN;
179 #ifdef DISABLE_DYNLOADING
180 ret = get_desktop_environment();
181 #else
182 OUString aModule(DESKTOP_DETECTOR_DLL_NAME);
183 oslModule aMod = osl_loadModuleRelative(
184 reinterpret_cast< oslGenericFunction >( &tryInstance ), aModule.pData,
185 SAL_LOADMODULE_DEFAULT );
186 if( aMod )
188 Fn_get_desktop_environment * pSym
189 = reinterpret_cast<Fn_get_desktop_environment *>(
190 osl_getAsciiFunctionSymbol(aMod, "get_desktop_environment"));
191 if( pSym )
192 ret = pSym();
194 osl_unloadModule( aMod );
195 #endif
196 return ret;
199 #if !STATIC_SAL_INSTANCE
200 const char* const* autodetect_plugin_list()
202 static const char* const pKDEFallbackList[] =
204 #if ENABLE_KF5
205 "kf5",
206 #endif
207 #if ENABLE_GTK3_KDE5
208 "gtk3_kde5",
209 #endif
210 #if ENABLE_GTK3
211 "gtk3",
212 #endif
213 #if ENABLE_GEN
214 "gen",
215 #endif
216 nullptr
219 static const char* const pStandardFallbackList[] =
221 #if ENABLE_GTK3
222 "gtk3",
223 #endif
224 #if ENABLE_GEN
225 "gen",
226 #endif
227 nullptr
230 #if ENABLE_HEADLESS
231 static const char* const pHeadlessFallbackList[] =
233 "svp",
234 nullptr
236 #endif
238 DesktopType desktop = lcl_get_desktop_environment();
239 const char * const * pList = pStandardFallbackList;
241 #if ENABLE_HEADLESS
242 // no server at all: dummy plugin
243 if ( desktop == DESKTOP_NONE )
244 pList = pHeadlessFallbackList;
245 else
246 #endif
247 if ( desktop == DESKTOP_GNOME ||
248 desktop == DESKTOP_UNITY ||
249 desktop == DESKTOP_XFCE ||
250 desktop == DESKTOP_MATE )
251 pList = pStandardFallbackList;
252 else if (desktop == DESKTOP_PLASMA5 || desktop == DESKTOP_LXQT)
253 pList = pKDEFallbackList;
255 return pList;
257 #endif // !STATIC_SAL_INSTANCE
258 #endif // UNIX_DESKTOP_DETECT
260 #endif // HAVE_FEATURE_UI
262 // HACK to obtain Application::IsHeadlessModeEnabled early on, before
263 // Application::EnableHeadlessMode has potentially been called:
264 bool IsHeadlessModeRequested()
266 if (Application::IsHeadlessModeEnabled()) {
267 return true;
269 sal_uInt32 n = rtl_getAppCommandArgCount();
270 for (sal_uInt32 i = 0; i < n; ++i) {
271 OUString arg;
272 rtl_getAppCommandArg(i, &arg.pData);
273 if ( arg == "--headless" || arg == "-headless" ) {
274 return true;
277 return false;
280 } // anonymous namespace
282 SalInstance *CreateSalInstance()
284 OUString aUsePlugin;
285 rtl::Bootstrap::get("SAL_USE_VCLPLUGIN", aUsePlugin);
286 SAL_INFO_IF(!aUsePlugin.isEmpty(), "vcl.plugadapt", "Requested VCL plugin: " << aUsePlugin);
288 if (Application::IsBitmapRendering() || (aUsePlugin.isEmpty() && IsHeadlessModeRequested()))
289 aUsePlugin = "svp";
291 if (aUsePlugin == "svp")
293 Application::EnableBitmapRendering();
294 #if ENABLE_HEADLESS
295 return svp_create_SalInstance();
296 #else
297 aUsePlugin.clear();
298 #endif
301 #if STATIC_SAL_INSTANCE
302 return create_SalInstance();
304 #else // !STATIC_SAL_INSTANCE
305 SalInstance *pInst = nullptr;
307 if( !aUsePlugin.isEmpty() )
308 pInst = tryInstance( aUsePlugin, true );
310 #if UNIX_DESKTOP_DETECT
311 const char* const* pPluginList = pInst ? nullptr : autodetect_plugin_list();
312 for (int i = 0; !pInst && pPluginList[i]; ++i)
314 pInst = tryInstance(OUString::createFromAscii(pPluginList[i]));
315 SAL_INFO_IF(pInst, "vcl.plugadapt", "plugin autodetection: " << pPluginList[i]);
317 #endif
319 // fallback, try everything
320 static const char* const pPlugin[] = {
321 #ifdef _WIN32
322 "win",
323 #elif defined(MACOSX)
324 "osx",
325 #else // !_WIN32 && !MACOSX
326 #if ENABLE_GTK3
327 "gtk3",
328 #endif
329 #if ENABLE_KF5
330 "kf5",
331 #endif
332 #if ENABLE_GTK3_KDE5
333 "gtk3_kde5",
334 #endif
335 #if ENABLE_GEN
336 "gen",
337 #endif
338 #if ENABLE_QT5
339 "qt5",
340 #endif
341 #if ENABLE_QT6
342 "qt6",
343 #endif
344 #endif // !_WIN32 && !MACOSX
345 nullptr
348 for (int i = 0; !pInst && pPlugin[i]; ++i)
349 pInst = tryInstance( OUString::createFromAscii( pPlugin[ i ] ) );
351 if( ! pInst )
353 std::fprintf( stderr, "no suitable windowing system found, exiting.\n" );
354 _exit( 1 );
357 return pInst;
358 #endif // !STATIC_SAL_INSTANCE
361 void DestroySalInstance( SalInstance *pInst )
363 delete pInst;
364 #if !STATIC_SAL_INSTANCE
365 if( pCloseModule )
366 osl_unloadModule( pCloseModule );
367 #endif
370 void SalAbort( const OUString& rErrorText, bool bDumpCore )
372 if (GetSalInstance())
373 GetSalInstance()->BeforeAbort(rErrorText, bDumpCore);
375 #if defined _WIN32
376 if( rErrorText.isEmpty() )
378 // make sure crash reporter is triggered
379 RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
380 FatalAppExitW( 0, L"Application Error" );
382 else
384 CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write);
385 // make sure crash reporter is triggered
386 RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
387 FatalAppExitW( 0, o3tl::toW(rErrorText.getStr()) );
389 #else // !_WIN32
390 #if defined ANDROID
391 OUString aError(rErrorText.isEmpty() ? "Unspecified application error" : rErrorText);
392 LOGE("SalAbort: '%s'", OUStringToOString(aError, osl_getThreadTextEncoding()).getStr());
393 #elif defined(iOS)
394 NSLog(@"SalAbort: %s", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr());
395 #else
396 if( rErrorText.isEmpty() )
397 std::fprintf( stderr, "Unspecified Application Error\n" );
398 else
400 CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write);
401 std::fprintf( stderr, "%s\n", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr() );
403 #endif
404 if( bDumpCore )
405 abort();
406 else
407 _exit(1);
408 #endif // !_WIN32
411 const OUString& SalGetDesktopEnvironment()
413 #if !HAVE_FEATURE_UI
414 static OUString aDesktopEnvironment("headless");
415 #elif defined(_WIN32)
416 static OUString aDesktopEnvironment( "Windows" );
417 #elif defined(MACOSX)
418 static OUString aDesktopEnvironment( "MacOSX" );
419 #elif defined(EMSCRIPTEN)
420 static OUString aDesktopEnvironment("WASM");
421 #elif defined(ANDROID)
422 static OUString aDesktopEnvironment("android");
423 #elif defined(iOS)
424 static OUString aDesktopEnvironment("iOS");
425 #elif UNIX_DESKTOP_DETECT
426 // Order to match desktops.hxx' DesktopType
427 static const char * const desktop_strings[] = {
428 "none", "unknown", "GNOME", "UNITY",
429 "XFCE", "MATE", "PLASMA5", "LXQT" };
430 static OUString aDesktopEnvironment;
431 if( aDesktopEnvironment.isEmpty())
433 aDesktopEnvironment = OUString::createFromAscii(
434 desktop_strings[lcl_get_desktop_environment()]);
436 #else
437 static OUString aDesktopEnvironment("unknown");
438 #endif
439 return aDesktopEnvironment;
442 #ifdef _WIN32
443 bool HasAtHook()
445 BOOL bIsRunning = FALSE;
446 // pvParam must be BOOL
447 return SystemParametersInfoW(SPI_GETSCREENREADER, 0, &bIsRunning, 0)
448 && bIsRunning;
450 #endif
452 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */