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  if( rErrorText.isEmpty() )
324  {
325  // make sure crash reporter is triggered
326  RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
327  FatalAppExitW( 0, L"Application Error" );
328  }
329  else
330  {
331  CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write);
332  // make sure crash reporter is triggered
333  RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
334  FatalAppExitW( 0, o3tl::toW(rErrorText.getStr()) );
335  }
336 #else
337 #if defined ANDROID
338  OUString aError(rErrorText.isEmpty() ? "Unspecified application error" : rErrorText);
339  LOGE("SalAbort: '%s'", OUStringToOString(aError, osl_getThreadTextEncoding()).getStr());
340 #else
341  if( rErrorText.isEmpty() )
342  std::fprintf( stderr, "Unspecified Application Error\n" );
343  else
344  {
345  CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write);
346  std::fprintf( stderr, "%s\n", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr() );
347  }
348 #endif
349  if( bDumpCore )
350  abort();
351  else
352  _exit(1);
353 #endif
354 }
355 
356 const OUString& SalGetDesktopEnvironment()
357 {
358 #ifdef _WIN32
359  static OUString aDesktopEnvironment( "Windows" );
360 #elif defined(MACOSX)
361  static OUString aDesktopEnvironment( "MacOSX" );
362 #elif defined(EMSCRIPTEN)
363  static OUString aDesktopEnvironment("WASM");
364 #elif defined(ANDROID)
365  static OUString aDesktopEnvironment("android");
366 #elif USING_X11
367  // Order to match desktops.hxx' DesktopType
368  static const char * const desktop_strings[] = {
369  "none", "unknown", "GNOME", "UNITY",
370  "XFCE", "MATE", "PLASMA5", "LXQT" };
371  static OUString aDesktopEnvironment;
372  if( aDesktopEnvironment.isEmpty())
373  {
374  aDesktopEnvironment = OUString::createFromAscii(
375  desktop_strings[lcl_get_desktop_environment()]);
376  }
377 #else
378  static OUString aDesktopEnvironment("unknown");
379 #endif
380  return aDesktopEnvironment;
381 }
382 
384  m_pInstance(nullptr),
385  m_pPIManager(nullptr)
386 {
387 }
388 
389 SalData::~SalData() COVERITY_NOEXCEPT_FALSE
390 {
391 #if (defined UNX && !defined MACOSX)
393 #endif
394 }
395 
396 #ifdef _WIN32
397 bool HasAtHook()
398 {
399  BOOL bIsRunning = FALSE;
400  // pvParam must be BOOL
401  return SystemParametersInfoW(SPI_GETSCREENREADER, 0, &bIsRunning, 0)
402  && bIsRunning;
403 }
404 #endif
405 
406 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const OUString & SalGetDesktopEnvironment()
Definition: salplug.cxx:356
#define SAL_DLLEXTENSION
static bool IsBitmapRendering()
Determines if bitmap rendering is enabled.
Definition: svapp.cxx:1565
#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:1448
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:210
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:1555
DESKTOP_UNITY
Definition: desktops.hxx:29
~SalData()
Definition: salplug.cxx:389
#define SAL_INFO(area, stream)
static void EnableBitmapRendering()
Enable software-only bitmap rendering.
Definition: svapp.cxx:1560
SalInstance *(* salFactoryProc)()
Definition: salplug.cxx:57
SalData()
Definition: salplug.cxx:383
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