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 <X11/Xlib.h>
21 
22 #include <unx/desktops.hxx>
23 
24 #include <rtl/bootstrap.hxx>
25 #include <rtl/process.h>
26 #include <osl/thread.h>
27 
28 #include <vclpluginapi.h>
29 
30 #include <string.h>
31 #include <comphelper/string.hxx>
32 
is_gnome_desktop(Display * pDisplay)33 static bool is_gnome_desktop( Display* pDisplay )
34 {
35 
36     // warning: these checks are coincidental, GNOME does not
37     // explicitly advertise itself
38     if ( getenv( "GNOME_DESKTOP_SESSION_ID" ) )
39         return true;
40 
41     bool ret = false;
42 
43     Atom nAtom1 = XInternAtom( pDisplay, "GNOME_SM_PROXY", True );
44     Atom nAtom2 = XInternAtom( pDisplay, "NAUTILUS_DESKTOP_WINDOW_ID", True );
45     if( nAtom1 || nAtom2 )
46     {
47         int nProperties = 0;
48         Atom* pProperties = XListProperties( pDisplay, DefaultRootWindow( pDisplay ), &nProperties );
49         if( pProperties && nProperties )
50         {
51             for( int i = 0; i < nProperties; i++ )
52                 if( pProperties[ i ] == nAtom1 ||
53                     pProperties[ i ] == nAtom2 )
54                 {
55                     ret = true;
56                     break;
57                 }
58             XFree( pProperties );
59         }
60     }
61     if (ret)
62         return true;
63 
64     Atom nUTFAtom       = XInternAtom( pDisplay, "UTF8_STRING", True );
65     Atom nNetWMNameAtom = XInternAtom( pDisplay, "_NET_WM_NAME", True );
66     if( nUTFAtom && nNetWMNameAtom )
67     {
68         // another, more expensive check: search for a gnome-panel
69         ::Window aRoot, aParent, *pChildren = nullptr;
70         unsigned int nChildren = 0;
71         XQueryTree( pDisplay, DefaultRootWindow( pDisplay ),
72                     &aRoot, &aParent, &pChildren, &nChildren );
73         if( pChildren && nChildren )
74         {
75             for( unsigned int i = 0; i < nChildren && ! ret; i++ )
76             {
77                 Atom nType = None;
78                 int nFormat = 0;
79                 unsigned long nItems = 0, nBytes = 0;
80                 unsigned char* pProp = nullptr;
81                 XGetWindowProperty( pDisplay,
82                                     pChildren[i],
83                                     nNetWMNameAtom,
84                                     0, 8,
85                                     False,
86                                     nUTFAtom,
87                                     &nType,
88                                     &nFormat,
89                                     &nItems,
90                                     &nBytes,
91                                     &pProp );
92                 if( pProp && nType == nUTFAtom )
93                 {
94                     OString aWMName( reinterpret_cast<char*>(pProp) );
95                     if (
96                         (aWMName.equalsIgnoreAsciiCase("gnome-shell")) ||
97                         (aWMName.equalsIgnoreAsciiCase("gnome-panel"))
98                        )
99                     {
100                         ret = true;
101                     }
102                 }
103                 if( pProp )
104                     XFree( pProp );
105             }
106             XFree( pChildren );
107         }
108     }
109 
110     return ret;
111 }
112 
is_plasma5_desktop()113 static bool is_plasma5_desktop()
114 {
115     static const char* pFullVersion = getenv("KDE_FULL_SESSION");
116     static const char* pSessionVersion = getenv("KDE_SESSION_VERSION");
117     return pFullVersion && pSessionVersion && (0 == strcmp(pSessionVersion, "5"));
118 }
119 
is_plasma6_desktop()120 static bool is_plasma6_desktop()
121 {
122     static const char* pFullVersion = getenv("KDE_FULL_SESSION");
123     static const char* pSessionVersion = getenv("KDE_SESSION_VERSION");
124     return pFullVersion && pSessionVersion && (0 == strcmp(pSessionVersion, "6"));
125 }
126 
get_desktop_environment()127 DesktopType get_desktop_environment()
128 {
129     static const char *pOverride = getenv( "OOO_FORCE_DESKTOP" );
130 
131     if ( pOverride && *pOverride )
132     {
133         OString aOver( pOverride );
134 
135         if ( aOver.equalsIgnoreAsciiCase( "lxqt" ) )
136             return DESKTOP_LXQT;
137         if (aOver.equalsIgnoreAsciiCase("plasma5") || aOver.equalsIgnoreAsciiCase("plasma"))
138             return DESKTOP_PLASMA5;
139         if (aOver.equalsIgnoreAsciiCase("plasma6"))
140             return DESKTOP_PLASMA6;
141         if ( aOver.equalsIgnoreAsciiCase( "gnome" ) )
142             return DESKTOP_GNOME;
143         if ( aOver.equalsIgnoreAsciiCase( "gnome-wayland" ) )
144             return DESKTOP_GNOME;
145         if ( aOver.equalsIgnoreAsciiCase( "unity" ) )
146             return DESKTOP_UNITY;
147         if ( aOver.equalsIgnoreAsciiCase( "xfce" ) )
148             return DESKTOP_XFCE;
149         if ( aOver.equalsIgnoreAsciiCase( "mate" ) )
150             return DESKTOP_MATE;
151         if ( aOver.equalsIgnoreAsciiCase( "none" ) )
152             return DESKTOP_UNKNOWN;
153     }
154 
155     OUString plugin;
156     rtl::Bootstrap::get(u"SAL_USE_VCLPLUGIN"_ustr, plugin);
157 
158     if (plugin == "svp")
159         return DESKTOP_NONE;
160 
161     const char *pDesktop = getenv( "XDG_CURRENT_DESKTOP" );
162     if ( pDesktop )
163     {
164         OString aCurrentDesktop( pDesktop, strlen( pDesktop ) );
165 
166         //it may be separated by colon ( e.g. unity:unity7:ubuntu )
167         std::vector<OUString> aSplitCurrentDesktop = comphelper::string::split(
168                 OStringToOUString( aCurrentDesktop, RTL_TEXTENCODING_UTF8), ':');
169         for (const auto& rCurrentDesktopStr : aSplitCurrentDesktop)
170         {
171             if ( rCurrentDesktopStr.equalsIgnoreAsciiCase( "unity" ) )
172                 return DESKTOP_UNITY;
173             else if ( rCurrentDesktopStr.equalsIgnoreAsciiCase( "gnome" ) )
174                 return DESKTOP_GNOME;
175             else if ( rCurrentDesktopStr.equalsIgnoreAsciiCase( "lxqt" ) )
176                 return DESKTOP_LXQT;
177         }
178     }
179 
180     const char *pSession = getenv( "DESKTOP_SESSION" );
181     OString aDesktopSession;
182     if ( pSession )
183         aDesktopSession = OString( pSession, strlen( pSession ) );
184 
185     // fast environment variable checks
186     if ( aDesktopSession.equalsIgnoreAsciiCase( "gnome" ) )
187         return DESKTOP_GNOME;
188     else if ( aDesktopSession.equalsIgnoreAsciiCase( "gnome-wayland" ) )
189         return DESKTOP_GNOME;
190     else if ( aDesktopSession.equalsIgnoreAsciiCase( "mate" ) )
191         return DESKTOP_MATE;
192     else if ( aDesktopSession.equalsIgnoreAsciiCase( "xfce" ) )
193         return DESKTOP_XFCE;
194     else if ( aDesktopSession.equalsIgnoreAsciiCase( "lxqt" ) )
195         return DESKTOP_LXQT;
196 
197     if (is_plasma5_desktop())
198         return DESKTOP_PLASMA5;
199     if (is_plasma6_desktop())
200         return DESKTOP_PLASMA6;
201 
202     // tdf#121275 if we still can't tell, and WAYLAND_DISPLAY
203     // is set, default to gtk3
204     const char* pWaylandStr = getenv("WAYLAND_DISPLAY");
205     if (pWaylandStr && *pWaylandStr)
206         return DESKTOP_GNOME;
207 
208     // these guys can be slower, with X property fetches,
209     // round-trips etc. and so are done later.
210 
211     // get display to connect to
212     const char* pDisplayStr = getenv( "DISPLAY" );
213 
214     int nParams = rtl_getAppCommandArgCount();
215     OUString aParam;
216     OString aBParm;
217     for( int i = 0; i < nParams; i++ )
218     {
219         rtl_getAppCommandArg( i, &aParam.pData );
220         if( i < nParams-1 && (aParam == "-display" || aParam == "--display" ) )
221         {
222             rtl_getAppCommandArg( i+1, &aParam.pData );
223             aBParm = OUStringToOString( aParam, osl_getThreadTextEncoding() );
224             pDisplayStr = aBParm.getStr();
225             break;
226         }
227     }
228 
229     // no server at all
230     if( ! pDisplayStr || !*pDisplayStr )
231         return DESKTOP_NONE;
232 
233 
234     /* #i92121# workaround deadlocks in the X11 implementation
235     */
236     static const char* pNoXInitThreads = getenv( "SAL_NO_XINITTHREADS" );
237     /* #i90094#
238        from now on we know that an X connection will be
239        established, so protect X against itself
240     */
241     if( ! ( pNoXInitThreads && *pNoXInitThreads ) )
242         XInitThreads();
243 
244     Display* pDisplay = XOpenDisplay( pDisplayStr );
245     if( pDisplay == nullptr )
246         return DESKTOP_NONE;
247 
248     DesktopType ret;
249     if ( is_gnome_desktop( pDisplay ) )
250         ret = DESKTOP_GNOME;
251     else
252         ret = DESKTOP_UNKNOWN;
253 
254     XCloseDisplay( pDisplay );
255 
256     return ret;
257 }
258 
259 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
260