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