/src/libreoffice/vcl/source/app/salplug.cxx
Line | Count | Source |
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 | | // The 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> |
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/DesktopType.hxx> |
42 | | #include <vcl/svapp.hxx> |
43 | | |
44 | | #if HAVE_FEATURE_UI |
45 | | #if USING_X11 |
46 | | #define UNIX_DESKTOP_DETECT 1 |
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 |
54 | | #include <staticsalinstance.hxx> |
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) |
66 | | #include <android/androidinst.hxx> |
67 | | #endif |
68 | | |
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 |
76 | | |
77 | | #if ENABLE_HEADLESS |
78 | | #include <headless/svpinst.hxx> |
79 | | #include <unx/gendata.hxx> |
80 | | #endif |
81 | | |
82 | | namespace { |
83 | | |
84 | | #if ENABLE_HEADLESS |
85 | | SalInstance* svp_create_SalInstance() |
86 | 101 | { |
87 | 101 | return new SvpSalInstance(std::make_unique<SvpSalYieldMutex>(), new GenericUnixSalData); |
88 | 101 | } |
89 | | #endif |
90 | | |
91 | | #if HAVE_FEATURE_UI |
92 | | |
93 | | #if !STATIC_SAL_INSTANCE |
94 | | oslModule pCloseModule = nullptr; |
95 | | |
96 | | extern "C" typedef SalInstance* (*salFactoryProc)(); |
97 | | |
98 | | SalInstance* 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 |
111 | | 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 == "kf6" || |
139 | | aUsedModuleBase == "qt5" || aUsedModuleBase == "qt6" || |
140 | | aUsedModuleBase == "win") |
141 | | { |
142 | | pCloseModule = nullptr; |
143 | | } |
144 | | } |
145 | | } |
146 | | else |
147 | | { |
148 | | SAL_WARN( |
149 | | "vcl.plugadapt", |
150 | | "could not load symbol create_SalInstance from shared object " |
151 | | << aModule); |
152 | | } |
153 | | } |
154 | | else if (bForce) |
155 | | { |
156 | | SAL_WARN("vcl.plugadapt", "could not load shared object " << aModule); |
157 | | } |
158 | | else |
159 | | { |
160 | | SAL_INFO("vcl.plugadapt", "could not load shared object " << aModule); |
161 | | } |
162 | | |
163 | | // coverity[leaked_storage] - this is on purpose |
164 | | return pInst; |
165 | | } |
166 | | #endif // !STATIC_SAL_INSTANCE |
167 | | |
168 | | #if UNIX_DESKTOP_DETECT |
169 | | |
170 | | #if !STATIC_SAL_INSTANCE |
171 | | std::vector<OUString> autodetect_plugin_list() |
172 | | { |
173 | | const DesktopType eDesktop = get_desktop_environment(); |
174 | | #if ENABLE_HEADLESS |
175 | | // no server at all: dummy plugin |
176 | | if (eDesktop == DesktopType::Headless) |
177 | | return { u"svp"_ustr }; |
178 | | #endif |
179 | | |
180 | | std::vector<OUString> aPlugins; |
181 | | if (eDesktop == DesktopType::LXQt || eDesktop == DesktopType::Plasma5 || eDesktop == DesktopType::Plasma6) |
182 | | { |
183 | | #if ENABLE_KF6 |
184 | | if (eDesktop == DesktopType::Plasma6) |
185 | | aPlugins.push_back(u"kf6"_ustr); |
186 | | #endif |
187 | | #if ENABLE_KF5 |
188 | | aPlugins.push_back(u"kf5"_ustr); |
189 | | #endif |
190 | | #if ENABLE_GTK3_KDE5 |
191 | | aPlugins.push_back(u"gtk3_kde5"_ustr); |
192 | | #endif |
193 | | } |
194 | | |
195 | | #if ENABLE_GTK3 |
196 | | aPlugins.push_back(u"gtk3"_ustr); |
197 | | #endif |
198 | | #if ENABLE_GEN |
199 | | aPlugins.push_back(u"gen"_ustr); |
200 | | #endif |
201 | | |
202 | | return aPlugins; |
203 | | } |
204 | | #endif // !STATIC_SAL_INSTANCE |
205 | | #endif // UNIX_DESKTOP_DETECT |
206 | | |
207 | | #endif // HAVE_FEATURE_UI |
208 | | |
209 | | // HACK to obtain Application::IsHeadlessModeEnabled early on, before |
210 | | // Application::EnableHeadlessMode has potentially been called: |
211 | | bool IsHeadlessModeRequested() |
212 | 0 | { |
213 | 0 | if (Application::IsHeadlessModeEnabled()) { |
214 | 0 | return true; |
215 | 0 | } |
216 | 0 | sal_uInt32 n = rtl_getAppCommandArgCount(); |
217 | 0 | for (sal_uInt32 i = 0; i < n; ++i) { |
218 | 0 | OUString arg; |
219 | 0 | rtl_getAppCommandArg(i, &arg.pData); |
220 | 0 | if ( arg == "--headless" || arg == "-headless" ) { |
221 | 0 | return true; |
222 | 0 | } |
223 | 0 | } |
224 | 0 | return false; |
225 | 0 | } |
226 | | |
227 | | } // anonymous namespace |
228 | | |
229 | | SalInstance *CreateSalInstance() |
230 | 101 | { |
231 | 101 | OUString aUsePlugin; |
232 | 101 | rtl::Bootstrap::get(u"SAL_USE_VCLPLUGIN"_ustr, aUsePlugin); |
233 | 101 | SAL_INFO_IF(!aUsePlugin.isEmpty(), "vcl.plugadapt", "Requested VCL plugin: " << aUsePlugin); |
234 | | |
235 | 101 | if (Application::IsBitmapRendering() || (aUsePlugin.isEmpty() && IsHeadlessModeRequested())) |
236 | 0 | aUsePlugin = "svp"; |
237 | | |
238 | 101 | if (aUsePlugin == "svp") |
239 | 101 | { |
240 | 101 | Application::EnableBitmapRendering(); |
241 | 101 | #if ENABLE_HEADLESS |
242 | 101 | return svp_create_SalInstance(); |
243 | | #else |
244 | | aUsePlugin.clear(); |
245 | | #endif |
246 | 101 | } |
247 | | |
248 | 0 | #if STATIC_SAL_INSTANCE |
249 | 0 | return create_SalInstance(); |
250 | | |
251 | | #else // !STATIC_SAL_INSTANCE |
252 | | |
253 | | if( !aUsePlugin.isEmpty() ) |
254 | | { |
255 | | if (SalInstance* pInst = tryInstance(aUsePlugin, true)) |
256 | | return pInst; |
257 | | } |
258 | | |
259 | | #if UNIX_DESKTOP_DETECT |
260 | | const std::vector<OUString> aPluginList = autodetect_plugin_list(); |
261 | | for (const OUString& rPlugin : aPluginList) |
262 | | { |
263 | | SalInstance* pInst = tryInstance(rPlugin); |
264 | | SAL_INFO_IF(pInst, "vcl.plugadapt", "plugin autodetection: " << rPlugin); |
265 | | if (pInst) |
266 | | return pInst; |
267 | | } |
268 | | #endif |
269 | | |
270 | | // fallback, try everything |
271 | | static constexpr OUString aPlugins[] = { |
272 | | #if defined(MACOSX) |
273 | | u"osx"_ustr, |
274 | | #endif |
275 | | #if ENABLE_WIN |
276 | | u"win"_ustr, |
277 | | #endif |
278 | | #if ENABLE_GTK3 |
279 | | u"gtk3"_ustr, |
280 | | #endif |
281 | | #if ENABLE_KF5 |
282 | | u"kf5"_ustr, |
283 | | #endif |
284 | | #if ENABLE_GTK3_KDE5 |
285 | | u"gtk3_kde5"_ustr, |
286 | | #endif |
287 | | #if ENABLE_GEN |
288 | | u"gen"_ustr, |
289 | | #endif |
290 | | #if ENABLE_QT5 |
291 | | u"qt5"_ustr, |
292 | | #endif |
293 | | #if ENABLE_KF6 |
294 | | u"kf6"_ustr, |
295 | | #endif |
296 | | #if ENABLE_QT6 |
297 | | u"qt6"_ustr, |
298 | | #endif |
299 | | }; |
300 | | |
301 | | for (const OUString& rPlugin : aPlugins) |
302 | | { |
303 | | if (SalInstance* pInst = tryInstance(rPlugin)) |
304 | | return pInst; |
305 | | } |
306 | | |
307 | | std::fprintf(stderr, "no suitable windowing system found, exiting.\n"); |
308 | | _exit(1); |
309 | | #endif // !STATIC_SAL_INSTANCE |
310 | 101 | } |
311 | | |
312 | | void DestroySalInstance( SalInstance *pInst ) |
313 | 0 | { |
314 | 0 | delete pInst; |
315 | | #if !STATIC_SAL_INSTANCE |
316 | | if( pCloseModule ) |
317 | | osl_unloadModule( pCloseModule ); |
318 | | #endif |
319 | 0 | } |
320 | | |
321 | | void SalAbort( const OUString& rErrorText, bool bDumpCore ) |
322 | 0 | { |
323 | 0 | if (GetSalInstance()) |
324 | 0 | GetSalInstance()->BeforeAbort(); |
325 | |
|
326 | | #if defined _WIN32 |
327 | | (void)bDumpCore; |
328 | | if( rErrorText.isEmpty() ) |
329 | | { |
330 | | // make sure crash reporter is triggered |
331 | | RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr ); |
332 | | FatalAppExitW( 0, L"Application Error" ); |
333 | | } |
334 | | else |
335 | | { |
336 | | CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write); |
337 | | // make sure crash reporter is triggered |
338 | | RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr ); |
339 | | FatalAppExitW( 0, o3tl::toW(rErrorText.getStr()) ); |
340 | | } |
341 | | #else // !_WIN32 |
342 | | #if defined ANDROID |
343 | | OUString aError(rErrorText.isEmpty() ? "Unspecified application error" : rErrorText); |
344 | | LOGE("SalAbort: '%s'", OUStringToOString(aError, osl_getThreadTextEncoding()).getStr()); |
345 | | #elif defined(iOS) |
346 | | NSLog(@"SalAbort: %s", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr()); |
347 | | #else |
348 | 0 | if( rErrorText.isEmpty() ) |
349 | 0 | std::fprintf( stderr, "Unspecified Application Error\n" ); |
350 | 0 | else |
351 | 0 | { |
352 | 0 | CrashReporter::addKeyValue(u"AbortMessage"_ustr, rErrorText, CrashReporter::Write); |
353 | 0 | std::fprintf( stderr, "%s\n", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr() ); |
354 | 0 | } |
355 | 0 | #endif |
356 | 0 | if( bDumpCore ) |
357 | 0 | abort(); |
358 | 0 | else |
359 | 0 | _exit(1); |
360 | 0 | #endif // !_WIN32 |
361 | 0 | } |
362 | | |
363 | | DesktopType SalGetDesktopEnvironment() |
364 | 0 | { |
365 | 0 | #if !HAVE_FEATURE_UI |
366 | 0 | return DesktopType::Headless; |
367 | | #elif defined(_WIN32) |
368 | | return DesktopType::Windows; |
369 | | #elif defined(MACOSX) |
370 | | return DesktopType::macOS; |
371 | | #elif defined(__EMSCRIPTEN__) |
372 | | return DesktopType::WASM; |
373 | | #elif defined(ANDROID) |
374 | | return DesktopType::Android; |
375 | | #elif defined(iOS) |
376 | | return DesktopType::iOS; |
377 | | #elif UNIX_DESKTOP_DETECT |
378 | | static DesktopType eDesktop = get_desktop_environment(); |
379 | | return eDesktop; |
380 | | #else |
381 | | return DesktopType::Unknown; |
382 | | #endif |
383 | 0 | } |
384 | | |
385 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |