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