LibreOffice Module vcl (master) 1
salplug.cxx
Go to the documentation of this file.
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// Th current high-level preprocessor structure is:
21//
22// if !HAVE_FEATURE_UI
23// => STATIC_SAL_INSTANCE
24// else
25// ? !STATIC_SAL_INSTANCE
26// ? UNIX_DESKTOP_DETECT
27// endif
28//
29// ENABLE_HEADLESS just signifies the use of the SVP plugin!
30
31#include <config_features.h>
32#include <config_vclplug.h>
33
34#include <cstdio>
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>
42
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
51
52#if defined(DISABLE_DYNLOADING) || !HAVE_FEATURE_UI
53#define STATIC_SAL_INSTANCE 1
55#else
56#define STATIC_SAL_INSTANCE 0
57#include <osl/module.hxx>
58#endif
59
60#if defined(iOS)
61#include <premac.h>
62#include <UIKit/UIKit.h>
63#include <postmac.h>
64
65#elif defined(ANDROID)
67#endif
68
69#if defined(_WIN32)
71#include <salframe.hxx>
72#include <Windows.h>
73#else
74#include <unistd.h>
75#endif
76
77#if ENABLE_HEADLESS
78#include <headless/svpdata.hxx>
79#include <headless/svpinst.hxx>
80#endif
81
82namespace {
83
84#if ENABLE_HEADLESS
85SalInstance* svp_create_SalInstance()
86{
87 SvpSalInstance* pInstance = new SvpSalInstance(std::make_unique<SvpSalYieldMutex>());
88 new SvpSalData();
89 return pInstance;
90}
91#endif
92
93#if HAVE_FEATURE_UI
94
95#if !STATIC_SAL_INSTANCE
96oslModule pCloseModule = nullptr;
97
98extern "C" typedef SalInstance* (*salFactoryProc)();
99
100SalInstance* tryInstance( const OUString& rModuleBase, bool bForce = false )
101{
102#if ENABLE_HEADLESS
103 if (rModuleBase == "svp")
104 return svp_create_SalInstance();
105#endif
106
107 SalInstance* pInst = nullptr;
108 OUString aUsedModuleBase(rModuleBase);
109 if (aUsedModuleBase == "kde5")
110 aUsedModuleBase = "kf5";
111 OUString aModule(
112#ifdef SAL_DLLPREFIX
114#endif
115 "vclplug_" + aUsedModuleBase + "lo" SAL_DLLEXTENSION );
116
117 osl::Module aMod;
118 if (aMod.loadRelative(reinterpret_cast<oslGenericFunction>(&tryInstance), aModule, SAL_LOADMODULE_GLOBAL))
119 {
120 salFactoryProc aProc = reinterpret_cast<salFactoryProc>(aMod.getFunctionSymbol("create_SalInstance"));
121 if (aProc)
122 {
123 pInst = aProc();
124 SAL_INFO(
125 "vcl.plugadapt",
126 "sal plugin " << aModule << " produced instance " << pInst);
127 if (pInst)
128 {
129 pCloseModule = static_cast<oslModule>(aMod);
130 aMod.release();
131
132 /*
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.
137 */
138 if (aUsedModuleBase == "gtk4" || aUsedModuleBase == "gtk3" ||
139 aUsedModuleBase == "gtk3_kde5" || aUsedModuleBase == "kf5" ||
140 aUsedModuleBase == "kf6" ||
141 aUsedModuleBase == "qt5" || aUsedModuleBase == "qt6" ||
142 aUsedModuleBase == "win")
143 {
144 pCloseModule = nullptr;
145 }
146 }
147 }
148 else
149 {
150 SAL_WARN(
151 "vcl.plugadapt",
152 "could not load symbol create_SalInstance from shared object "
153 << aModule);
154 }
155 }
156 else if (bForce)
157 {
158 SAL_WARN("vcl.plugadapt", "could not load shared object " << aModule);
159 }
160 else
161 {
162 SAL_INFO("vcl.plugadapt", "could not load shared object " << aModule);
163 }
164
165 // coverity[leaked_storage] - this is on purpose
166 return pInst;
167}
168#endif // !STATIC_SAL_INSTANCE
169
170#if UNIX_DESKTOP_DETECT
171#ifndef DISABLE_DYNLOADING
172extern "C" typedef DesktopType Fn_get_desktop_environment();
173#else
174extern "C" DesktopType get_desktop_environment();
175#endif
176
177DesktopType lcl_get_desktop_environment()
178{
180#ifdef DISABLE_DYNLOADING
181 ret = get_desktop_environment();
182#else
183 OUString aModule(DESKTOP_DETECTOR_DLL_NAME);
184 oslModule aMod = osl_loadModuleRelative(
185 reinterpret_cast< oslGenericFunction >( &tryInstance ), aModule.pData,
186 SAL_LOADMODULE_DEFAULT );
187 if( aMod )
188 {
189 Fn_get_desktop_environment * pSym
190 = reinterpret_cast<Fn_get_desktop_environment *>(
191 osl_getAsciiFunctionSymbol(aMod, "get_desktop_environment"));
192 if( pSym )
193 ret = pSym();
194 }
195 osl_unloadModule( aMod );
196#endif
197 return ret;
198}
199
200#if !STATIC_SAL_INSTANCE
201const char* const* autodetect_plugin_list()
202{
203 static const char* const pKDEFallbackList[] =
204 {
205#if ENABLE_KF5
206 "kf5",
207#endif
208#if ENABLE_GTK3_KDE5
209 "gtk3_kde5",
210#endif
211#if ENABLE_GTK3
212 "gtk3",
213#endif
214#if ENABLE_GEN
215 "gen",
216#endif
217 nullptr
218 };
219
220 static const char* const pPlasma6FallbackList[] =
221 {
222#if ENABLE_KF6
223 "kf6",
224#endif
225#if ENABLE_KF5
226 "kf5",
227#endif
228#if ENABLE_GTK3_KDE5
229 "gtk3_kde5",
230#endif
231#if ENABLE_GTK3
232 "gtk3",
233#endif
234#if ENABLE_GEN
235 "gen",
236#endif
237 nullptr
238 };
239
240 static const char* const pStandardFallbackList[] =
241 {
242#if ENABLE_GTK3
243 "gtk3",
244#endif
245#if ENABLE_GEN
246 "gen",
247#endif
248 nullptr
249 };
250
251#if ENABLE_HEADLESS
252 static const char* const pHeadlessFallbackList[] =
253 {
254 "svp",
255 nullptr
256 };
257#endif
258
259 DesktopType desktop = lcl_get_desktop_environment();
260 const char * const * pList = pStandardFallbackList;
261
262#if ENABLE_HEADLESS
263 // no server at all: dummy plugin
264 if ( desktop == DESKTOP_NONE )
265 pList = pHeadlessFallbackList;
266 else
267#endif
268 if ( desktop == DESKTOP_GNOME ||
272 pList = pStandardFallbackList;
273 else if (desktop == DESKTOP_PLASMA5 || desktop == DESKTOP_LXQT)
274 pList = pKDEFallbackList;
275 else if (desktop == DESKTOP_PLASMA6)
276 pList = pPlasma6FallbackList;
277
278 return pList;
279}
280#endif // !STATIC_SAL_INSTANCE
281#endif // UNIX_DESKTOP_DETECT
282
283#endif // HAVE_FEATURE_UI
284
285// HACK to obtain Application::IsHeadlessModeEnabled early on, before
286// Application::EnableHeadlessMode has potentially been called:
287bool IsHeadlessModeRequested()
288{
290 return true;
291 }
292 sal_uInt32 n = rtl_getAppCommandArgCount();
293 for (sal_uInt32 i = 0; i < n; ++i) {
294 OUString arg;
295 rtl_getAppCommandArg(i, &arg.pData);
296 if ( arg == "--headless" || arg == "-headless" ) {
297 return true;
298 }
299 }
300 return false;
301}
302
303} // anonymous namespace
304
306{
307 OUString aUsePlugin;
308 rtl::Bootstrap::get("SAL_USE_VCLPLUGIN", aUsePlugin);
309 SAL_INFO_IF(!aUsePlugin.isEmpty(), "vcl.plugadapt", "Requested VCL plugin: " << aUsePlugin);
310
311 if (Application::IsBitmapRendering() || (aUsePlugin.isEmpty() && IsHeadlessModeRequested()))
312 aUsePlugin = "svp";
313
314 if (aUsePlugin == "svp")
315 {
317#if ENABLE_HEADLESS
318 return svp_create_SalInstance();
319#else
320 aUsePlugin.clear();
321#endif
322 }
323
324#if STATIC_SAL_INSTANCE
325 return create_SalInstance();
326
327#else // !STATIC_SAL_INSTANCE
328 SalInstance *pInst = nullptr;
329
330 if( !aUsePlugin.isEmpty() )
331 pInst = tryInstance( aUsePlugin, true );
332
333#if UNIX_DESKTOP_DETECT
334 const char* const* pPluginList = pInst ? nullptr : autodetect_plugin_list();
335 for (int i = 0; !pInst && pPluginList[i]; ++i)
336 {
337 pInst = tryInstance(OUString::createFromAscii(pPluginList[i]));
338 SAL_INFO_IF(pInst, "vcl.plugadapt", "plugin autodetection: " << pPluginList[i]);
339 }
340#endif
341
342 // fallback, try everything
343 static const char* const pPlugin[] = {
344#ifdef _WIN32
345 "win",
346#elif defined(MACOSX)
347 "osx",
348#else // !_WIN32 && !MACOSX
349#if ENABLE_GTK3
350 "gtk3",
351#endif
352#if ENABLE_KF5
353 "kf5",
354#endif
355#if ENABLE_GTK3_KDE5
356 "gtk3_kde5",
357#endif
358#if ENABLE_GEN
359 "gen",
360#endif
361#if ENABLE_QT5
362 "qt5",
363#endif
364#if ENABLE_KF6
365 "kf6",
366#endif
367#if ENABLE_QT6
368 "qt6",
369#endif
370#endif // !_WIN32 && !MACOSX
371 nullptr
372 };
373
374 for (int i = 0; !pInst && pPlugin[i]; ++i)
375 pInst = tryInstance( OUString::createFromAscii( pPlugin[ i ] ) );
376
377 if( ! pInst )
378 {
379 std::fprintf( stderr, "no suitable windowing system found, exiting.\n" );
380 _exit( 1 );
381 }
382
383 return pInst;
384#endif // !STATIC_SAL_INSTANCE
385}
386
388{
389 delete pInst;
390#if !STATIC_SAL_INSTANCE
391 if( pCloseModule )
392 osl_unloadModule( pCloseModule );
393#endif
394}
395
396void SalAbort( const OUString& rErrorText, bool bDumpCore )
397{
398 if (GetSalInstance())
399 GetSalInstance()->BeforeAbort(rErrorText, bDumpCore);
400
401#if defined _WIN32
402 if( rErrorText.isEmpty() )
403 {
404 // make sure crash reporter is triggered
405 RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
406 FatalAppExitW( 0, L"Application Error" );
407 }
408 else
409 {
410 CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write);
411 // make sure crash reporter is triggered
412 RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
413 FatalAppExitW( 0, o3tl::toW(rErrorText.getStr()) );
414 }
415#else // !_WIN32
416#if defined ANDROID
417 OUString aError(rErrorText.isEmpty() ? "Unspecified application error" : rErrorText);
418 LOGE("SalAbort: '%s'", OUStringToOString(aError, osl_getThreadTextEncoding()).getStr());
419#elif defined(iOS)
420 NSLog(@"SalAbort: %s", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr());
421#else
422 if( rErrorText.isEmpty() )
423 std::fprintf( stderr, "Unspecified Application Error\n" );
424 else
425 {
426 CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write);
427 std::fprintf( stderr, "%s\n", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr() );
428 }
429#endif
430 if( bDumpCore )
431 abort();
432 else
433 _exit(1);
434#endif // !_WIN32
435}
436
438{
439#if !HAVE_FEATURE_UI
440 static OUString aDesktopEnvironment("headless");
441#elif defined(_WIN32)
442 static OUString aDesktopEnvironment( "Windows" );
443#elif defined(MACOSX)
444 static OUString aDesktopEnvironment( "MacOSX" );
445#elif defined(EMSCRIPTEN)
446 static OUString aDesktopEnvironment("WASM");
447#elif defined(ANDROID)
448 static OUString aDesktopEnvironment("android");
449#elif defined(iOS)
450 static OUString aDesktopEnvironment("iOS");
451#elif UNIX_DESKTOP_DETECT
452 // Order to match desktops.hxx' DesktopType
453 static const char * const desktop_strings[] = {
454 "none", "unknown", "GNOME", "UNITY",
455 "XFCE", "MATE", "PLASMA5", "PLASMA6", "LXQT" };
456 static OUString aDesktopEnvironment;
457 if( aDesktopEnvironment.isEmpty())
458 {
459 aDesktopEnvironment = OUString::createFromAscii(
460 desktop_strings[lcl_get_desktop_environment()]);
461 }
462#else
463 static OUString aDesktopEnvironment("unknown");
464#endif
465 return aDesktopEnvironment;
466}
467
468#ifdef _WIN32
469bool HasAtHook()
470{
471 BOOL bIsRunning = FALSE;
472 // pvParam must be BOOL
473 return SystemParametersInfoW(SPI_GETSCREENREADER, 0, &bIsRunning, 0)
474 && bIsRunning;
475}
476#endif
477
478/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define LOGE(...)
Definition: androidinst.hxx:21
#define SAL_DLLPREFIX
Definition: builder.cxx:1443
static bool IsBitmapRendering()
Determines if bitmap rendering is enabled.
Definition: svapp.cxx:1549
static void EnableBitmapRendering()
Enable software-only bitmap rendering.
Definition: svapp.cxx:1544
static bool IsHeadlessModeEnabled()
Determines if headless mode is enabled.
Definition: svapp.cxx:1539
static void addKeyValue(SAL_UNUSED_PARAMETER const OUString &, SAL_UNUSED_PARAMETER const OUString &, SAL_UNUSED_PARAMETER tAddKeyHandling)
virtual void BeforeAbort(const OUString &, bool)
Definition: salinst.hxx:213
DESKTOP_UNITY
Definition: desktops.hxx:32
DESKTOP_PLASMA6
Definition: desktops.hxx:36
enum SAL_DLLPUBLIC_RTTI DesktopType
Definition: desktops.hxx:28
DESKTOP_NONE
Definition: desktops.hxx:29
DESKTOP_PLASMA5
Definition: desktops.hxx:35
DESKTOP_UNKNOWN
Definition: desktops.hxx:30
DESKTOP_MATE
Definition: desktops.hxx:34
DESKTOP_GNOME
Definition: desktops.hxx:31
DESKTOP_XFCE
Definition: desktops.hxx:33
#define FALSE
sal_Int64 n
#define SAL_INFO_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
int i
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
const wchar_t *typedef BOOL
void SalAbort(const OUString &rErrorText, bool bDumpCore)
Definition: salplug.cxx:396
SalInstance * create_SalInstance()
const OUString & SalGetDesktopEnvironment()
Definition: salplug.cxx:437
void DestroySalInstance(SalInstance *pInst)
Definition: salplug.cxx:387
SalInstance * CreateSalInstance()
Definition: salplug.cxx:305
SalInstance * GetSalInstance()
Definition: svdata.hxx:473