Line data Source code
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 <prex.h>
21 : #include <X11/Xatom.h>
22 : #include <postx.h>
23 :
24 : #include <unx/desktops.hxx>
25 :
26 : #include "rtl/bootstrap.hxx"
27 : #include "rtl/process.h"
28 : #include "rtl/ustrbuf.hxx"
29 : #include "osl/module.h"
30 : #include "osl/thread.h"
31 :
32 : #include "vclpluginapi.h"
33 :
34 : #include <unistd.h>
35 : #include <string.h>
36 :
37 6 : static bool is_gnome_desktop( Display* pDisplay )
38 : {
39 6 : bool ret = false;
40 :
41 : // warning: these checks are coincidental, GNOME does not
42 : // explicitly advertise itself
43 6 : if ( NULL != getenv( "GNOME_DESKTOP_SESSION_ID" ) )
44 0 : ret = true;
45 :
46 6 : if( ! ret )
47 : {
48 6 : Atom nAtom1 = XInternAtom( pDisplay, "GNOME_SM_PROXY", True );
49 6 : Atom nAtom2 = XInternAtom( pDisplay, "NAUTILUS_DESKTOP_WINDOW_ID", True );
50 6 : if( nAtom1 || nAtom2 )
51 : {
52 6 : int nProperties = 0;
53 6 : Atom* pProperties = XListProperties( pDisplay, DefaultRootWindow( pDisplay ), &nProperties );
54 6 : if( pProperties && nProperties )
55 : {
56 114 : for( int i = 0; i < nProperties; i++ )
57 216 : if( pProperties[ i ] == nAtom1 ||
58 108 : pProperties[ i ] == nAtom2 )
59 : {
60 0 : ret = true;
61 : }
62 6 : XFree( pProperties );
63 : }
64 : }
65 : }
66 :
67 6 : if( ! ret )
68 : {
69 6 : Atom nUTFAtom = XInternAtom( pDisplay, "UTF8_STRING", True );
70 6 : Atom nNetWMNameAtom = XInternAtom( pDisplay, "_NET_WM_NAME", True );
71 6 : if( nUTFAtom && nNetWMNameAtom )
72 : {
73 : // another, more expensive check: search for a gnome-panel
74 6 : ::Window aRoot, aParent, *pChildren = NULL;
75 6 : unsigned int nChildren = 0;
76 6 : XQueryTree( pDisplay, DefaultRootWindow( pDisplay ),
77 6 : &aRoot, &aParent, &pChildren, &nChildren );
78 6 : if( pChildren && nChildren )
79 : {
80 277 : for( unsigned int i = 0; i < nChildren && ! ret; i++ )
81 : {
82 271 : Atom nType = None;
83 271 : int nFormat = 0;
84 271 : unsigned long nItems = 0, nBytes = 0;
85 271 : unsigned char* pProp = NULL;
86 : XGetWindowProperty( pDisplay,
87 271 : pChildren[i],
88 : nNetWMNameAtom,
89 : 0, 8,
90 : False,
91 : nUTFAtom,
92 : &nType,
93 : &nFormat,
94 : &nItems,
95 : &nBytes,
96 271 : &pProp );
97 271 : if( pProp && nType == nUTFAtom )
98 : {
99 39 : OString aWMName( reinterpret_cast<char*>(pProp) );
100 39 : if (
101 78 : (aWMName.equalsIgnoreAsciiCase("gnome-shell")) ||
102 39 : (aWMName.equalsIgnoreAsciiCase("gnome-panel"))
103 : )
104 : {
105 0 : ret = true;
106 39 : }
107 : }
108 271 : if( pProp )
109 39 : XFree( pProp );
110 : }
111 6 : XFree( pChildren );
112 : }
113 : }
114 : }
115 :
116 6 : return ret;
117 : }
118 :
119 : static bool bWasXError = false;
120 :
121 0 : static inline bool WasXError()
122 : {
123 0 : bool bRet = bWasXError;
124 0 : bWasXError = false;
125 0 : return bRet;
126 : }
127 :
128 : extern "C"
129 : {
130 0 : static int autodect_error_handler( Display*, XErrorEvent* )
131 : {
132 0 : bWasXError = true;
133 0 : return 0;
134 : }
135 :
136 : typedef int(* XErrorHandler)(Display*,XErrorEvent*);
137 : }
138 :
139 6 : static int TDEVersion( Display* pDisplay )
140 : {
141 6 : int nRet = 0;
142 :
143 6 : Atom nFullSession = XInternAtom( pDisplay, "TDE_FULL_SESSION", True );
144 6 : Atom nTDEVersion = XInternAtom( pDisplay, "TDE_SESSION_VERSION", True );
145 :
146 6 : if( nFullSession )
147 : {
148 0 : if( !nTDEVersion )
149 0 : return 14;
150 :
151 0 : Atom aRealType = None;
152 0 : int nFormat = 8;
153 0 : unsigned long nItems = 0;
154 0 : unsigned long nBytesLeft = 0;
155 0 : unsigned char* pProperty = NULL;
156 : XGetWindowProperty( pDisplay,
157 0 : DefaultRootWindow( pDisplay ),
158 : nTDEVersion,
159 : 0, 1,
160 : False,
161 : AnyPropertyType,
162 : &aRealType,
163 : &nFormat,
164 : &nItems,
165 : &nBytesLeft,
166 0 : &pProperty );
167 0 : if( !WasXError() && nItems != 0 && pProperty )
168 : {
169 0 : nRet = *reinterpret_cast< sal_Int32* >( pProperty );
170 : }
171 0 : if( pProperty )
172 : {
173 0 : XFree( pProperty );
174 0 : pProperty = NULL;
175 : }
176 : }
177 6 : return nRet;
178 : }
179 :
180 18 : static int KDEVersion( Display* pDisplay )
181 : {
182 18 : int nRet = 0;
183 :
184 18 : Atom nFullSession = XInternAtom( pDisplay, "KDE_FULL_SESSION", True );
185 18 : Atom nKDEVersion = XInternAtom( pDisplay, "KDE_SESSION_VERSION", True );
186 :
187 18 : if( nFullSession )
188 : {
189 0 : if( !nKDEVersion )
190 0 : return 3;
191 :
192 0 : Atom aRealType = None;
193 0 : int nFormat = 8;
194 0 : unsigned long nItems = 0;
195 0 : unsigned long nBytesLeft = 0;
196 0 : unsigned char* pProperty = NULL;
197 : XGetWindowProperty( pDisplay,
198 0 : DefaultRootWindow( pDisplay ),
199 : nKDEVersion,
200 : 0, 1,
201 : False,
202 : AnyPropertyType,
203 : &aRealType,
204 : &nFormat,
205 : &nItems,
206 : &nBytesLeft,
207 0 : &pProperty );
208 0 : if( !WasXError() && nItems != 0 && pProperty )
209 : {
210 0 : nRet = *reinterpret_cast< sal_Int32* >( pProperty );
211 : }
212 0 : if( pProperty )
213 : {
214 0 : XFree( pProperty );
215 0 : pProperty = NULL;
216 : }
217 : }
218 18 : return nRet;
219 : }
220 :
221 6 : static bool is_tde_desktop( Display* pDisplay )
222 : {
223 6 : if ( NULL != getenv( "TDE_FULL_SESSION" ) )
224 : {
225 0 : return true; // TDE
226 : }
227 :
228 6 : if ( TDEVersion( pDisplay ) >= 14 )
229 0 : return true;
230 :
231 6 : return false;
232 : }
233 :
234 6 : static bool is_kde_desktop( Display* pDisplay )
235 : {
236 6 : if ( NULL != getenv( "KDE_FULL_SESSION" ) )
237 : {
238 0 : const char *pVer = getenv( "KDE_SESSION_VERSION" );
239 0 : if ( !pVer || pVer[0] == '0' )
240 : {
241 0 : return true; // does not exist => KDE3
242 : }
243 :
244 0 : OUString aVer( "3" );
245 0 : if ( aVer.equalsIgnoreAsciiCaseAscii( pVer ) )
246 : {
247 0 : return true;
248 0 : }
249 : }
250 :
251 6 : if ( KDEVersion( pDisplay ) == 3 )
252 0 : return true;
253 :
254 6 : return false;
255 : }
256 :
257 6 : static bool is_kde4_desktop( Display* pDisplay )
258 : {
259 6 : if ( NULL != getenv( "KDE_FULL_SESSION" ) )
260 : {
261 0 : OUString aVer( "4" );
262 :
263 0 : const char *pVer = getenv( "KDE_SESSION_VERSION" );
264 0 : if ( pVer && aVer.equalsIgnoreAsciiCaseAscii( pVer ) )
265 0 : return true;
266 : }
267 :
268 6 : if ( KDEVersion( pDisplay ) == 4 )
269 0 : return true;
270 :
271 6 : return false;
272 : }
273 :
274 6 : static bool is_kde5_desktop( Display* pDisplay )
275 : {
276 6 : if ( NULL != getenv( "KDE_FULL_SESSION" ) )
277 : {
278 0 : OUString aVer( "5" );
279 :
280 0 : const char *pVer = getenv( "KDE_SESSION_VERSION" );
281 0 : if ( pVer && aVer.equalsIgnoreAsciiCaseAscii( pVer ) )
282 0 : return true;
283 : }
284 :
285 6 : if ( KDEVersion( pDisplay ) == 5 )
286 0 : return true;
287 :
288 6 : return false;
289 : }
290 :
291 : extern "C"
292 : {
293 :
294 6 : DESKTOP_DETECTOR_PUBLIC DesktopType get_desktop_environment()
295 : {
296 6 : static const char *pOverride = getenv( "OOO_FORCE_DESKTOP" );
297 :
298 6 : if ( pOverride && *pOverride )
299 : {
300 0 : OString aOver( pOverride );
301 :
302 0 : if ( aOver.equalsIgnoreAsciiCase( "tde" ) )
303 0 : return DESKTOP_TDE;
304 0 : if ( aOver.equalsIgnoreAsciiCase( "kde5" ) )
305 0 : return DESKTOP_KDE5;
306 0 : if ( aOver.equalsIgnoreAsciiCase( "kde4" ) )
307 0 : return DESKTOP_KDE4;
308 0 : if ( aOver.equalsIgnoreAsciiCase( "gnome" ) )
309 0 : return DESKTOP_GNOME;
310 0 : if ( aOver.equalsIgnoreAsciiCase( "unity" ) )
311 0 : return DESKTOP_UNITY;
312 0 : if ( aOver.equalsIgnoreAsciiCase( "xfce" ) )
313 0 : return DESKTOP_XFCE;
314 0 : if ( aOver.equalsIgnoreAsciiCase( "mate" ) )
315 0 : return DESKTOP_MATE;
316 0 : if ( aOver.equalsIgnoreAsciiCase( "kde" ) )
317 0 : return DESKTOP_KDE;
318 0 : if ( aOver.equalsIgnoreAsciiCase( "none" ) )
319 0 : return DESKTOP_UNKNOWN;
320 : }
321 :
322 : // get display to connect to
323 6 : const char* pDisplayStr = getenv( "DISPLAY" );
324 :
325 6 : OUString plugin;
326 6 : rtl::Bootstrap::get("SAL_USE_VCLPLUGIN", plugin);
327 :
328 6 : if (plugin == "svp")
329 0 : pDisplayStr = NULL;
330 : else
331 : {
332 6 : int nParams = rtl_getAppCommandArgCount();
333 6 : OUString aParam;
334 12 : OString aBParm;
335 26 : for( int i = 0; i < nParams; i++ )
336 : {
337 20 : rtl_getAppCommandArg( i, &aParam.pData );
338 20 : if( i < nParams-1 && (aParam == "-display" || aParam == "--display" ) )
339 : {
340 0 : rtl_getAppCommandArg( i+1, &aParam.pData );
341 0 : aBParm = OUStringToOString( aParam, osl_getThreadTextEncoding() );
342 0 : pDisplayStr = aBParm.getStr();
343 0 : break;
344 : }
345 6 : }
346 : }
347 :
348 : // no server at all
349 6 : if( ! pDisplayStr || !*pDisplayStr )
350 0 : return DESKTOP_NONE;
351 :
352 : /* #i92121# workaround deadlocks in the X11 implementation
353 : */
354 6 : static const char* pNoXInitThreads = getenv( "SAL_NO_XINITTHREADS" );
355 : /* #i90094#
356 : from now on we know that an X connection will be
357 : established, so protect X against itself
358 : */
359 6 : if( ! ( pNoXInitThreads && *pNoXInitThreads ) )
360 6 : XInitThreads();
361 :
362 6 : Display* pDisplay = XOpenDisplay( pDisplayStr );
363 6 : if( pDisplay == NULL )
364 0 : return DESKTOP_NONE;
365 :
366 : DesktopType ret;
367 :
368 6 : XErrorHandler pOldHdl = XSetErrorHandler( autodect_error_handler );
369 :
370 : const char *pSession;
371 12 : OString aDesktopSession;
372 6 : if ( ( pSession = getenv( "DESKTOP_SESSION" ) ) )
373 0 : aDesktopSession = OString( pSession, strlen( pSession ) );
374 :
375 : const char *pDesktop;
376 12 : OString aCurrentDesktop;
377 6 : if ( ( pDesktop = getenv( "XDG_CURRENT_DESKTOP" ) ) )
378 0 : aCurrentDesktop = OString( pDesktop, strlen( pDesktop ) );
379 :
380 : // fast environment variable checks
381 6 : if ( aCurrentDesktop.equalsIgnoreAsciiCase( "unity" ) )
382 0 : ret = DESKTOP_UNITY;
383 6 : else if ( aDesktopSession.equalsIgnoreAsciiCase( "gnome" ) )
384 0 : ret = DESKTOP_GNOME;
385 6 : else if ( aDesktopSession.equalsIgnoreAsciiCase( "mate" ) )
386 0 : ret = DESKTOP_MATE;
387 6 : else if ( aDesktopSession.equalsIgnoreAsciiCase( "xfce" ) )
388 0 : ret = DESKTOP_XFCE;
389 :
390 : // these guys can be slower, with X property fetches,
391 : // round-trips etc. and so are done later.
392 6 : else if ( is_kde5_desktop( pDisplay ) )
393 0 : ret = DESKTOP_KDE5;
394 6 : else if ( is_kde4_desktop( pDisplay ) )
395 0 : ret = DESKTOP_KDE4;
396 6 : else if ( is_gnome_desktop( pDisplay ) )
397 0 : ret = DESKTOP_GNOME;
398 6 : else if ( is_kde_desktop( pDisplay ) )
399 0 : ret = DESKTOP_KDE;
400 6 : else if ( is_tde_desktop( pDisplay ) )
401 0 : ret = DESKTOP_TDE;
402 : else
403 6 : ret = DESKTOP_UNKNOWN;
404 :
405 : // set the default handler again
406 6 : XSetErrorHandler( pOldHdl );
407 :
408 6 : XCloseDisplay( pDisplay );
409 :
410 12 : return ret;
411 : }
412 :
413 : }
414 :
415 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|