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 #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>
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
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 == "kf5" ||
111  aUsedModuleBase == "qt5" || aUsedModuleBase == "win")
112  {
113  pCloseModule = nullptr;
114  }
115  }
116  }
117  else
118  {
119  SAL_WARN(
120  "vcl.plugadapt",
121  "could not load symbol create_SalInstance from shared object "
122  << aModule);
123  }
124  }
125  else if (bForce)
126  {
127  SAL_WARN("vcl.plugadapt", "could not load shared object " << aModule);
128  }
129  else
130  {
131  SAL_INFO("vcl.plugadapt", "could not load shared object " << aModule);
132  }
133 
134  // coverity[leaked_storage] - this is on purpose
135  return pInst;
136 #endif // !DISABLE_DYNLOADING
137 }
138 
139 #ifdef DESKTOPDETECT
140 #ifndef DISABLE_DYNLOADING
141 extern "C" typedef DesktopType Fn_get_desktop_environment();
142 #endif
143 
144 DesktopType lcl_get_desktop_environment()
145 {
147 #ifdef DISABLE_DYNLOADING
148  ret = get_desktop_environment();
149 #else
150  OUString aModule(DESKTOP_DETECTOR_DLL_NAME);
151  oslModule aMod = osl_loadModuleRelative(
152  reinterpret_cast< oslGenericFunction >( &tryInstance ), aModule.pData,
153  SAL_LOADMODULE_DEFAULT );
154  if( aMod )
155  {
156  Fn_get_desktop_environment * pSym
157  = reinterpret_cast<Fn_get_desktop_environment *>(
158  osl_getAsciiFunctionSymbol(aMod, "get_desktop_environment"));
159  if( pSym )
160  ret = pSym();
161  }
162  osl_unloadModule( aMod );
163 #endif
164  return ret;
165 }
166 
167 SalInstance* autodetect_plugin()
168 {
169 #ifdef DISABLE_DYNLOADING
170  return nullptr;
171 #else // !DISABLE_DYNLOADING
172  static const char* const pKDEFallbackList[] =
173  {
174 #if ENABLE_KF5
175  "kf5",
176 #endif
177 #if ENABLE_GTK3_KDE5
178  "gtk3_kde5",
179 #endif
180  "gtk3", "gen", nullptr
181  };
182 
183  static const char* const pStandardFallbackList[] =
184  {
185  "gtk3", "gen", nullptr
186  };
187 
188 #ifdef HEADLESS_VCLPLUG
189  static const char* const pHeadlessFallbackList[] =
190  {
191  "svp", nullptr
192  };
193 #endif
194 
195  SalInstance* pInst = nullptr;
196  DesktopType desktop = lcl_get_desktop_environment();
197  const char * const * pList = pStandardFallbackList;
198  int nListEntry = 0;
199 
200 #ifdef HEADLESS_VCLPLUG
201  // no server at all: dummy plugin
202  if ( desktop == DESKTOP_NONE )
203  pList = pHeadlessFallbackList;
204  else
205 #endif
206  if ( desktop == DESKTOP_GNOME ||
207  desktop == DESKTOP_UNITY ||
208  desktop == DESKTOP_XFCE ||
209  desktop == DESKTOP_MATE )
210  pList = pStandardFallbackList;
211  else if (desktop == DESKTOP_PLASMA5 || desktop == DESKTOP_LXQT)
212  pList = pKDEFallbackList;
213 
214  while( pList[nListEntry] && pInst == nullptr )
215  {
216  OUString aTry( OUString::createFromAscii( pList[nListEntry] ) );
217  pInst = tryInstance( aTry );
218  SAL_INFO_IF(
219  pInst, "vcl.plugadapt",
220  "plugin autodetection: " << pList[nListEntry]);
221  nListEntry++;
222  }
223  return pInst;
224 #endif // !DISABLE_DYNLOADING
225 }
226 #endif // DESKTOPDETECT
227 
228 #ifdef HEADLESS_VCLPLUG
229 // HACK to obtain Application::IsHeadlessModeEnabled early on, before
230 // Application::EnableHeadlessMode has potentially been called:
231 bool IsHeadlessModeRequested()
232 {
234  return true;
235  }
236  sal_uInt32 n = rtl_getAppCommandArgCount();
237  for (sal_uInt32 i = 0; i < n; ++i) {
238  OUString arg;
239  rtl_getAppCommandArg(i, &arg.pData);
240  if ( arg == "--headless" || arg == "-headless" ) {
241  return true;
242  }
243  }
244  return false;
245 }
246 #endif
247 
248 } // anonymous namespace
249 
251 {
252  SalInstance *pInst = nullptr;
253  OUString aUsePlugin;
254  rtl::Bootstrap::get("SAL_USE_VCLPLUGIN", aUsePlugin);
255  SAL_INFO_IF(!aUsePlugin.isEmpty(), "vcl", "Requested VCL plugin: " << aUsePlugin);
256 #ifdef HEADLESS_VCLPLUG
257  if (Application::IsBitmapRendering() || (aUsePlugin.isEmpty() && IsHeadlessModeRequested()))
258  aUsePlugin = "svp";
259 #endif
260 
261  if (aUsePlugin == "svp")
262  {
264 #ifndef HEADLESS_VCLPLUG
265  aUsePlugin.clear();
266 #endif
267  }
268 
269  if( !aUsePlugin.isEmpty() )
270  pInst = tryInstance( aUsePlugin, true );
271 
272 #ifdef DESKTOPDETECT
273  if( ! pInst )
274  pInst = autodetect_plugin();
275 #endif
276 
277  // fallback, try everything
278  static const char* const pPlugin[] = {
279 #ifdef _WIN32
280  "win"
281 #else
282 #ifdef MACOSX
283  "osx"
284 #else
285  "gtk3", "kf5", "gen"
286 #endif
287 #endif
288  };
289 
290  for ( int i = 0; !pInst && i != SAL_N_ELEMENTS(pPlugin); ++i )
291  pInst = tryInstance( OUString::createFromAscii( pPlugin[ i ] ) );
292 
293  if( ! pInst )
294  {
295  std::fprintf( stderr, "no suitable windowing system found, exiting.\n" );
296  _exit( 1 );
297  }
298 
299  // acquire SolarMutex
300  pInst->AcquireYieldMutex();
301 
302  return pInst;
303 }
304 
306 {
307  // release SolarMutex
308  pInst->ReleaseYieldMutexAll();
309 
310  delete pInst;
311 #ifndef DISABLE_DYNLOADING
312  if( pCloseModule )
313  osl_unloadModule( pCloseModule );
314 #endif
315 }
316 
317 void SalAbort( const OUString& rErrorText, bool bDumpCore )
318 {
319  if (GetSalData()->m_pInstance)
320  GetSalData()->m_pInstance->BeforeAbort(rErrorText, bDumpCore);
321 
322 #if defined _WIN32
323  (void) bDumpCore;
324  if( rErrorText.isEmpty() )
325  {
326  // make sure crash reporter is triggered
327  RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
328  FatalAppExitW( 0, L"Application Error" );
329  }
330  else
331  {
332  CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write);
333  // make sure crash reporter is triggered
334  RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
335  FatalAppExitW( 0, o3tl::toW(rErrorText.getStr()) );
336  }
337 #else
338 #if defined ANDROID
339  OUString aError(rErrorText.isEmpty() ? "Unspecified application error" : rErrorText);
340  LOGE("SalAbort: '%s'", OUStringToOString(aError, osl_getThreadTextEncoding()).getStr());
341 #else
342  if( rErrorText.isEmpty() )
343  std::fprintf( stderr, "Unspecified Application Error\n" );
344  else
345  {
346  CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write);
347  std::fprintf( stderr, "%s\n", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr() );
348  }
349 #endif
350  if( bDumpCore )
351  abort();
352  else
353  _exit(1);
354 #endif
355 }
356 
357 const OUString& SalGetDesktopEnvironment()
358 {
359 #ifdef _WIN32
360  static OUString aDesktopEnvironment( "Windows" );
361 #elif defined(MACOSX)
362  static OUString aDesktopEnvironment( "MacOSX" );
363 #elif defined(EMSCRIPTEN)
364  static OUString aDesktopEnvironment("WASM");
365 #elif defined(ANDROID)
366  static OUString aDesktopEnvironment("android");
367 #elif USING_X11
368  // Order to match desktops.hxx' DesktopType
369  static const char * const desktop_strings[] = {
370  "none", "unknown", "GNOME", "UNITY",
371  "XFCE", "MATE", "PLASMA5", "LXQT" };
372  static OUString aDesktopEnvironment;
373  if( aDesktopEnvironment.isEmpty())
374  {
375  aDesktopEnvironment = OUString::createFromAscii(
376  desktop_strings[lcl_get_desktop_environment()]);
377  }
378 #else
379  static OUString aDesktopEnvironment("unknown");
380 #endif
381  return aDesktopEnvironment;
382 }
383 
385  m_pInstance(nullptr),
386  m_pPIManager(nullptr)
387 {
388 }
389 
390 SalData::~SalData() COVERITY_NOEXCEPT_FALSE
391 {
392 #if (defined UNX && !defined MACOSX)
394 #endif
395 }
396 
397 #ifdef _WIN32
398 bool HasAtHook()
399 {
400  BOOL bIsRunning = FALSE;
401  // pvParam must be BOOL
402  return SystemParametersInfoW(SPI_GETSCREENREADER, 0, &bIsRunning, 0)
403  && bIsRunning;
404 }
405 #endif
406 
407 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const OUString & SalGetDesktopEnvironment()
Definition: salplug.cxx:357
#define SAL_DLLEXTENSION
static bool IsBitmapRendering()
Determines if bitmap rendering is enabled.
Definition: svapp.cxx:1556
#define SAL_INFO_IF(condition, area, stream)
DESKTOP_UNKNOWN
Definition: desktops.hxx:29
sal_Int64 n
DESKTOP_PLASMA5
Definition: desktops.hxx:29
DESKTOP_XFCE
Definition: desktops.hxx:29
DESKTOP_MATE
Definition: desktops.hxx:29
#define SAL_DLLPREFIX
Definition: builder.cxx:1489
void DestroySalInstance(SalInstance *pInst)
Definition: salplug.cxx:305
void AcquireYieldMutex(sal_uInt32 nCount=1)
Definition: salvtables.cxx:122
const wchar_t *typedef BOOL
void SalAbort(const OUString &rErrorText, bool bDumpCore)
Definition: salplug.cxx:317
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
#define SAL_N_ELEMENTS(arr)
SalInstance * m_pInstance
int i
virtual void BeforeAbort(const OUString &, bool)
Definition: salinst.hxx:209
SalData * GetSalData()
DESKTOP_GNOME
Definition: desktops.hxx:29
sal_uInt32 ReleaseYieldMutexAll()
Definition: salvtables.cxx:120
enum SAL_DLLPUBLIC_RTTI DesktopType
Definition: desktops.hxx:27
SalInstance * svp_create_SalInstance()
SalInstance * CreateSalInstance()
Definition: salplug.cxx:250
static bool IsHeadlessModeEnabled()
Determines if headless mode is enabled.
Definition: svapp.cxx:1546
DESKTOP_UNITY
Definition: desktops.hxx:29
~SalData()
Definition: salplug.cxx:390
#define SAL_INFO(area, stream)
static void EnableBitmapRendering()
Enable software-only bitmap rendering.
Definition: svapp.cxx:1551
SalInstance *(* salFactoryProc)()
Definition: salplug.cxx:57
SalData()
Definition: salplug.cxx:384
DESKTOP_NONE
Definition: desktops.hxx:29
#define FALSE
#define SAL_WARN(area, stream)
#define LOGE(...)
Definition: androidinst.hxx:22
static void addKeyValue(SAL_UNUSED_PARAMETER const OUString &, SAL_UNUSED_PARAMETER const OUString &, SAL_UNUSED_PARAMETER tAddKeyHandling)
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo