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 #error "Android has no plugin infrastructure!"
47 #endif
48 
49 #if !(defined _WIN32 || defined MACOSX)
50 #define DESKTOPDETECT
51 #define HEADLESS_VCLPLUG
52 #endif
53 
54 extern "C" {
55 typedef SalInstance*(*salFactoryProc)();
56 }
57 
58 namespace {
59 
60 oslModule pCloseModule = nullptr;
61 
62 SalInstance* tryInstance( const OUString& rModuleBase, bool bForce = false )
63 {
64 #ifdef HEADLESS_VCLPLUG
65  if (rModuleBase == "svp")
66  return svp_create_SalInstance();
67 #endif
68 
69  SalInstance* pInst = nullptr;
70  OUString aUsedModuleBase(rModuleBase);
71  if (aUsedModuleBase == "kde5")
72  aUsedModuleBase = "kf5";
73  OUString aModule(
74 #ifdef SAL_DLLPREFIX
76 #endif
77  "vclplug_" + aUsedModuleBase + "lo" SAL_DLLEXTENSION );
78 
79  osl::Module aMod;
80  if (aMod.loadRelative(reinterpret_cast<oslGenericFunction>(&tryInstance), aModule, SAL_LOADMODULE_GLOBAL))
81  {
82  salFactoryProc aProc = reinterpret_cast<salFactoryProc>(aMod.getFunctionSymbol("create_SalInstance"));
83  if (aProc)
84  {
85  pInst = aProc();
86  SAL_INFO(
87  "vcl.plugadapt",
88  "sal plugin " << aModule << " produced instance " << pInst);
89  if (pInst)
90  {
91  pCloseModule = static_cast<oslModule>(aMod);
92  aMod.release();
93 
94  /*
95  * Recent GTK+ versions load their modules with RTLD_LOCAL, so we can
96  * not access the 'gnome_accessibility_module_shutdown' anymore.
97  * So make sure libgtk+ & co are still mapped into memory when
98  * atk-bridge's atexit handler gets called.
99  */
100  if( aUsedModuleBase == "gtk3" || aUsedModuleBase == "gtk3_kde5" || aUsedModuleBase == "win" )
101  {
102  pCloseModule = nullptr;
103  }
104  }
105  }
106  else
107  {
108  SAL_WARN(
109  "vcl.plugadapt",
110  "could not load symbol create_SalInstance from shared object "
111  << aModule);
112  }
113  }
114  else if (bForce)
115  {
116  SAL_WARN("vcl.plugadapt", "could not load shared object " << aModule);
117  }
118  else
119  {
120  SAL_INFO("vcl.plugadapt", "could not load shared object " << aModule);
121  }
122 
123  // coverity[leaked_storage] - this is on purpose
124  return pInst;
125 }
126 
127 #ifdef DESKTOPDETECT
128 extern "C" typedef DesktopType Fn_get_desktop_environment();
129 
130 DesktopType get_desktop_environment()
131 {
132  OUString aModule(DESKTOP_DETECTOR_DLL_NAME);
133  oslModule aMod = osl_loadModuleRelative(
134  reinterpret_cast< oslGenericFunction >( &tryInstance ), aModule.pData,
135  SAL_LOADMODULE_DEFAULT );
137  if( aMod )
138  {
139  Fn_get_desktop_environment * pSym
140  = reinterpret_cast<Fn_get_desktop_environment *>(
141  osl_getAsciiFunctionSymbol(aMod, "get_desktop_environment"));
142  if( pSym )
143  ret = pSym();
144  }
145  osl_unloadModule( aMod );
146  return ret;
147 }
148 
149 SalInstance* autodetect_plugin()
150 {
151  static const char* const pKDEFallbackList[] =
152  {
153 #if ENABLE_KF5
154  "kf5",
155 #endif
156 #if ENABLE_GTK3_KDE5
157  "gtk3_kde5",
158 #endif
159  "gtk3", "gen", nullptr
160  };
161 
162  static const char* const pStandardFallbackList[] =
163  {
164  "gtk3", "gen", nullptr
165  };
166 
167 #ifdef HEADLESS_VCLPLUG
168  static const char* const pHeadlessFallbackList[] =
169  {
170  "svp", nullptr
171  };
172 #endif
173 
174  DesktopType desktop = get_desktop_environment();
175  const char * const * pList = pStandardFallbackList;
176  int nListEntry = 0;
177 
178 #ifdef HEADLESS_VCLPLUG
179  // no server at all: dummy plugin
180  if ( desktop == DESKTOP_NONE )
181  pList = pHeadlessFallbackList;
182  else
183 #endif
184  if ( desktop == DESKTOP_GNOME ||
185  desktop == DESKTOP_UNITY ||
186  desktop == DESKTOP_XFCE ||
187  desktop == DESKTOP_MATE )
188  pList = pStandardFallbackList;
189  else if (desktop == DESKTOP_PLASMA5 || desktop == DESKTOP_LXQT)
190  pList = pKDEFallbackList;
191 
192  SalInstance* pInst = nullptr;
193  while( pList[nListEntry] && pInst == nullptr )
194  {
195  OUString aTry( OUString::createFromAscii( pList[nListEntry] ) );
196  pInst = tryInstance( aTry );
197  SAL_INFO_IF(
198  pInst, "vcl.plugadapt",
199  "plugin autodetection: " << pList[nListEntry]);
200  nListEntry++;
201  }
202 
203  return pInst;
204 }
205 #endif // DESKTOPDETECT
206 
207 #ifdef HEADLESS_VCLPLUG
208 // HACK to obtain Application::IsHeadlessModeEnabled early on, before
209 // Application::EnableHeadlessMode has potentially been called:
210 bool IsHeadlessModeRequested()
211 {
213  return true;
214  }
215  sal_uInt32 n = rtl_getAppCommandArgCount();
216  for (sal_uInt32 i = 0; i < n; ++i) {
217  OUString arg;
218  rtl_getAppCommandArg(i, &arg.pData);
219  if ( arg == "--headless" || arg == "-headless" ) {
220  return true;
221  }
222  }
223  return false;
224 }
225 #endif
226 
227 } // anonymous namespace
228 
230 {
231  SalInstance *pInst = nullptr;
232 
233  OUString aUsePlugin;
234  rtl::Bootstrap::get("SAL_USE_VCLPLUGIN", aUsePlugin);
235  SAL_INFO_IF(!aUsePlugin.isEmpty(), "vcl", "Requested VCL plugin: " << aUsePlugin);
236 #ifdef HEADLESS_VCLPLUG
237  if (Application::IsBitmapRendering() || (aUsePlugin.isEmpty() && IsHeadlessModeRequested()))
238  aUsePlugin = "svp";
239 #endif
240 
241  if (aUsePlugin == "svp")
242  {
244 #ifndef HEADLESS_VCLPLUG
245  aUsePlugin.clear();
246 #endif
247  }
248  if( !aUsePlugin.isEmpty() )
249  pInst = tryInstance( aUsePlugin, true );
250 
251 #ifdef DESKTOPDETECT
252  if( ! pInst )
253  pInst = autodetect_plugin();
254 #endif
255 
256  // fallback, try everything
257  static const char* const pPlugin[] = {
258 #ifdef _WIN32
259  "win"
260 #else
261 #ifdef MACOSX
262  "osx"
263 #else
264  "gtk3", "kf5", "gen"
265 #endif
266 #endif
267  };
268 
269  for ( int i = 0; !pInst && i != SAL_N_ELEMENTS(pPlugin); ++i )
270  pInst = tryInstance( OUString::createFromAscii( pPlugin[ i ] ) );
271 
272  if( ! pInst )
273  {
274  std::fprintf( stderr, "no suitable windowing system found, exiting.\n" );
275  _exit( 1 );
276  }
277 
278  // acquire SolarMutex
279  pInst->AcquireYieldMutex();
280 
281  return pInst;
282 }
283 
285 {
286  // release SolarMutex
287  pInst->ReleaseYieldMutexAll();
288 
289  delete pInst;
290  if( pCloseModule )
291  osl_unloadModule( pCloseModule );
292 }
293 
294 void SalAbort( const OUString& rErrorText, bool bDumpCore )
295 {
296 #if defined _WIN32
297  //TODO: ImplFreeSalGDI();
298 #endif
299 
300  if( rErrorText.isEmpty() )
301  {
302 #if defined _WIN32
303  // make sure crash reporter is triggered
304  RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
305  FatalAppExitW( 0, L"Application Error" );
306 #else
307  std::fprintf( stderr, "Application Error\n" );
308 #endif
309  }
310  else
311  {
312  CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write);
313 #if defined _WIN32
314  // make sure crash reporter is triggered
315  RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
316  FatalAppExitW( 0, o3tl::toW(rErrorText.getStr()) );
317 #else
318  std::fprintf( stderr, "%s\n", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr() );
319 #endif
320  }
321 #if defined _WIN32
322  (void) bDumpCore;
323 #else
324  if( bDumpCore )
325  abort();
326  else
327  _exit(1);
328 #endif
329 }
330 
331 const OUString& SalGetDesktopEnvironment()
332 {
333 #ifdef _WIN32
334  static OUString aDesktopEnvironment( "Windows" );
335 
336 #else
337 #ifdef MACOSX
338  static OUString aDesktopEnvironment( "MacOSX" );
339 #else
340  // Order to match desktops.hxx' DesktopType
341  static const char * const desktop_strings[] = {
342  "none", "unknown", "GNOME", "UNITY",
343  "XFCE", "MATE", "PLASMA5", "LXQT" };
344  static OUString aDesktopEnvironment;
345  if( aDesktopEnvironment.isEmpty())
346  {
347  aDesktopEnvironment = OUString::createFromAscii(
348  desktop_strings[get_desktop_environment()]);
349  }
350 #endif
351 #endif
352  return aDesktopEnvironment;
353 }
354 
356  m_pInstance(nullptr),
357  m_pPIManager(nullptr)
358 {
359 }
360 
361 SalData::~SalData() COVERITY_NOEXCEPT_FALSE
362 {
363 #if (defined UNX && !defined MACOSX)
365 #endif
366 }
367 
368 #ifdef _WIN32
369 bool HasAtHook()
370 {
371  BOOL bIsRunning = FALSE;
372  // pvParam must be BOOL
373  return SystemParametersInfoW(SPI_GETSCREENREADER, 0, &bIsRunning, 0)
374  && bIsRunning;
375 }
376 #endif
377 
378 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const OUString & SalGetDesktopEnvironment()
Definition: salplug.cxx:331
#define SAL_DLLEXTENSION
static bool IsBitmapRendering()
Determines if bitmap rendering is enabled.
Definition: svapp.cxx:1551
#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:1481
void DestroySalInstance(SalInstance *pInst)
Definition: salplug.cxx:284
void AcquireYieldMutex(sal_uInt32 nCount=1)
Definition: salvtables.cxx:121
const wchar_t *typedef BOOL
void SalAbort(const OUString &rErrorText, bool bDumpCore)
Definition: salplug.cxx:294
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
#define SAL_N_ELEMENTS(arr)
int i
DESKTOP_GNOME
Definition: desktops.hxx:29
sal_uInt32 ReleaseYieldMutexAll()
Definition: salvtables.cxx:119
enum SAL_DLLPUBLIC_RTTI DesktopType
Definition: desktops.hxx:27
SalInstance * svp_create_SalInstance()
SalInstance * CreateSalInstance()
Definition: salplug.cxx:229
static bool IsHeadlessModeEnabled()
Determines if headless mode is enabled.
Definition: svapp.cxx:1541
DESKTOP_UNITY
Definition: desktops.hxx:29
~SalData()
Definition: salplug.cxx:361
#define SAL_INFO(area, stream)
static void EnableBitmapRendering()
Enable software-only bitmap rendering.
Definition: svapp.cxx:1546
SalInstance *(* salFactoryProc)()
Definition: salplug.cxx:55
SalData()
Definition: salplug.cxx:355
DESKTOP_NONE
Definition: desktops.hxx:29
#define FALSE
#define SAL_WARN(area, stream)
static void addKeyValue(SAL_UNUSED_PARAMETER const OUString &, SAL_UNUSED_PARAMETER const OUString &, SAL_UNUSED_PARAMETER tAddKeyHandling)
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo