[WebVR] Add OpenVR to the tree and to the build
[WebKit-https.git] / Source / ThirdParty / openvr / src / vrcommon / vrpathregistry_public.cpp
1 //========= Copyright Valve Corporation ============//
2
3 #include "vrpathregistry_public.h"
4 #include "json/json.h"
5 #include "pathtools_public.h"
6 #include "envvartools_public.h"
7 #include "strtools_public.h"
8 #include "dirtools_public.h"
9
10 #if defined( WIN32 )
11 #include <windows.h>
12 #include <shlobj.h>
13
14 #undef GetEnvironmentVariable
15 #elif defined OSX
16 #include <Foundation/Foundation.h>
17 #include <AppKit/AppKit.h>
18 #elif defined(LINUX)
19 #include <dlfcn.h>
20 #include <stdio.h>
21 #endif
22
23 #include <algorithm>
24
25 #ifndef VRLog
26        #if defined( __MINGW32__ )
27               #define VRLog(args...)              fprintf(stderr, args)
28        #elif defined( WIN32 )
29               #define VRLog(fmt, ...)              fprintf(stderr, fmt, __VA_ARGS__)
30        #else
31               #define VRLog(args...)              fprintf(stderr, args)
32        #endif
33 #endif
34
35 /** Returns the root of the directory the system wants us to store user config data in */
36 static std::string GetAppSettingsPath()
37 {
38 #if defined( WIN32 )
39        WCHAR rwchPath[MAX_PATH];
40
41        if( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, rwchPath ) ) )
42        {
43               return "";
44        }
45
46        // Convert the path to UTF-8 and store in the output
47        std::string sUserPath = UTF16to8( rwchPath );
48
49        return sUserPath;
50 #elif defined( OSX )
51        std::string sSettingsDir;
52        @autoreleasepool {
53               // Search for the path
54               NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES );
55               if ( [paths count] == 0 )
56               {
57                      return "";
58               }
59               
60               NSString *resolvedPath = [paths objectAtIndex:0];
61               resolvedPath = [resolvedPath stringByAppendingPathComponent: @"OpenVR"];
62               
63               if ( ![[NSFileManager new] createDirectoryAtPath: resolvedPath withIntermediateDirectories:YES attributes:nil error:nil] )
64               {
65                      return "";
66               }
67               
68               sSettingsDir.assign( [resolvedPath UTF8String] );
69        }
70        return sSettingsDir;
71 #elif defined( LINUX )
72
73        // As defined by XDG Base Directory Specification 
74        // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
75
76        const char *pchHome = getenv("XDG_CONFIG_HOME");
77        if ( ( pchHome != NULL) && ( pchHome[0] != '\0' ) )
78        {
79               return pchHome;
80        }
81
82        //
83        // XDG_CONFIG_HOME is not defined, use ~/.config instead
84        // 
85        pchHome = getenv( "HOME" );
86        if ( pchHome == NULL )
87        {
88               return "";
89        }
90
91        std::string sUserPath( pchHome );
92        sUserPath = Path_Join( sUserPath, ".config" );
93        return sUserPath;
94 #else
95        #warning "Unsupported platform"
96 #endif
97 }
98
99
100 // ---------------------------------------------------------------------------
101 // Purpose: Constructor
102 // ---------------------------------------------------------------------------
103 CVRPathRegistry_Public::CVRPathRegistry_Public()
104 {
105
106 }
107
108 // ---------------------------------------------------------------------------
109 // Purpose: Computes the registry filename
110 // ---------------------------------------------------------------------------
111 std::string CVRPathRegistry_Public::GetOpenVRConfigPath()
112 {
113        std::string sConfigPath = GetAppSettingsPath();
114        if( sConfigPath.empty() )
115               return "";
116
117 #if defined( _WIN32 ) || defined( LINUX )
118        sConfigPath = Path_Join( sConfigPath, "openvr" );
119 #elif defined ( OSX ) 
120        sConfigPath = Path_Join( sConfigPath, ".openvr" );
121 #else
122        #warning "Unsupported platform"
123 #endif
124        sConfigPath = Path_FixSlashes( sConfigPath );
125        return sConfigPath;
126 }
127
128
129
130 //-----------------------------------------------------------------------------
131 // Purpose:
132 //-----------------------------------------------------------------------------
133 std::string CVRPathRegistry_Public::GetVRPathRegistryFilename()
134 {
135        std::string sPath = GetOpenVRConfigPath();
136        if ( sPath.empty() )
137               return "";
138
139 #if defined( _WIN32 )
140        sPath = Path_Join( sPath, "openvrpaths.vrpath" );
141 #elif defined ( POSIX ) 
142        sPath = Path_Join( sPath, "openvrpaths.vrpath" );
143 #else
144        #error "Unsupported platform"
145 #endif
146        sPath = Path_FixSlashes( sPath );
147        return sPath;
148 }
149
150
151 // ---------------------------------------------------------------------------
152 // Purpose: Converts JSON to a history array
153 // ---------------------------------------------------------------------------
154 static void ParseStringListFromJson( std::vector< std::string > *pvecHistory, const Json::Value & root, const char *pchArrayName )
155 {
156        if( !root.isMember( pchArrayName ) )
157               return;
158
159        const Json::Value & arrayNode = root[ pchArrayName ];
160        if( !arrayNode )
161        {
162               VRLog( "VR Path Registry node %s is not an array\n", pchArrayName );
163               return;
164        }
165
166        pvecHistory->clear();
167        pvecHistory->reserve( arrayNode.size() );
168        for( uint32_t unIndex = 0; unIndex < arrayNode.size(); unIndex++ )
169        {
170               std::string sPath( arrayNode[ unIndex ].asString() );
171               pvecHistory->push_back( sPath );
172        }
173 }
174
175
176 // ---------------------------------------------------------------------------
177 // Purpose: Converts a history array to JSON
178 // ---------------------------------------------------------------------------
179 static void StringListToJson( const std::vector< std::string > & vecHistory, Json::Value & root, const char *pchArrayName )
180 {
181        Json::Value & arrayNode = root[ pchArrayName ];
182        for( auto i = vecHistory.begin(); i != vecHistory.end(); i++ )
183        {
184               arrayNode.append( *i );
185        }
186 }
187
188
189 //-----------------------------------------------------------------------------
190 // Purpose:
191 //-----------------------------------------------------------------------------
192 bool CVRPathRegistry_Public::ToJsonString( std::string &sJsonString )
193 {
194        std::string sRegPath = GetVRPathRegistryFilename();
195        if( sRegPath.empty() )
196               return false;
197        
198        std::string sRegistryContents = Path_ReadTextFile( sRegPath );
199        if( sRegistryContents.empty() )
200               return false;
201
202        sJsonString = sRegistryContents;
203
204        return true;
205 }
206
207
208 // ---------------------------------------------------------------------------
209 // Purpose: Loads the config file from its well known location
210 // ---------------------------------------------------------------------------
211 bool CVRPathRegistry_Public::BLoadFromFile()
212 {
213        std::string sRegPath = GetVRPathRegistryFilename();
214        if( sRegPath.empty() )
215        {
216               VRLog( "Unable to determine VR Path Registry filename\n" );
217               return false;
218        }
219
220        std::string sRegistryContents = Path_ReadTextFile( sRegPath );
221        if( sRegistryContents.empty() )
222        {
223               VRLog( "Unable to read VR Path Registry from %s\n", sRegPath.c_str() );
224               return false;
225        }
226
227        Json::Value root;
228        Json::Reader reader;
229
230        if( !reader.parse( sRegistryContents, root ) )
231        {
232               VRLog( "Unable to parse %s: %s\n", sRegPath.c_str(), reader.getFormattedErrorMessages().c_str() );
233               return false;
234        }
235
236        ParseStringListFromJson( &m_vecRuntimePath, root, "runtime" );
237        ParseStringListFromJson( &m_vecConfigPath, root, "config" );
238        ParseStringListFromJson( &m_vecLogPath, root, "log" );
239        if (root.isMember( "external_drivers" ) && root[ "external_drivers" ].isArray() )
240        {
241               ParseStringListFromJson( &m_vecExternalDrivers, root, "external_drivers" );
242        }
243
244        return true;
245 }
246
247
248 // ---------------------------------------------------------------------------
249 // Purpose: Saves the config file to its well known location
250 // ---------------------------------------------------------------------------
251 bool CVRPathRegistry_Public::BSaveToFile() const
252 {
253 #if defined( DASHBOARD_BUILD_MODE )
254        return false;
255 #else
256        std::string sRegPath = GetVRPathRegistryFilename();
257        if( sRegPath.empty() )
258               return false;
259        
260        Json::Value root;
261        
262        root[ "version" ] = 1;
263        root[ "jsonid" ] = "vrpathreg";
264
265        StringListToJson( m_vecRuntimePath, root, "runtime" );
266        StringListToJson( m_vecConfigPath, root, "config" );
267        StringListToJson( m_vecLogPath, root, "log" );
268        StringListToJson( m_vecExternalDrivers, root, "external_drivers" );
269
270        Json::StyledWriter writer;
271        std::string sRegistryContents = writer.write( root );
272
273        // make sure the directory we're writing into actually exists
274        std::string sRegDirectory = Path_StripFilename( sRegPath );
275        if( !BCreateDirectoryRecursive( sRegDirectory.c_str() ) )
276        {
277               VRLog( "Unable to create path registry directory %s\n", sRegDirectory.c_str() );
278               return false;
279        }
280
281        if( !Path_WriteStringToTextFile( sRegPath, sRegistryContents.c_str() ) )
282        {
283               VRLog( "Unable to write VR path registry to %s\n", sRegPath.c_str() );
284               return false;
285        }
286
287        return true;
288 #endif
289 }
290
291
292 // ---------------------------------------------------------------------------
293 // Purpose: Returns the current runtime path or NULL if no path is configured.
294 // ---------------------------------------------------------------------------
295 std::string CVRPathRegistry_Public::GetRuntimePath() const
296 {
297        if( m_vecRuntimePath.empty() )
298               return "";
299        else
300               return m_vecRuntimePath.front().c_str();
301 }
302
303
304 // ---------------------------------------------------------------------------
305 // Purpose: Returns the current config path or NULL if no path is configured.
306 // ---------------------------------------------------------------------------
307 std::string CVRPathRegistry_Public::GetConfigPath() const
308 {
309        if( m_vecConfigPath.empty() )
310               return "";
311        else
312               return m_vecConfigPath.front().c_str();
313 }
314
315
316 // ---------------------------------------------------------------------------
317 // Purpose: Returns the current log path or NULL if no path is configured.
318 // ---------------------------------------------------------------------------
319 std::string CVRPathRegistry_Public::GetLogPath() const
320 {
321        if( m_vecLogPath.empty() )
322               return "";
323        else
324               return m_vecLogPath.front().c_str();
325 }
326
327
328
329 // ---------------------------------------------------------------------------
330 // Purpose: Returns paths using the path registry and the provided override 
331 //                     values. Pass NULL for any paths you don't care about.
332 // ---------------------------------------------------------------------------
333 bool CVRPathRegistry_Public::GetPaths( std::string *psRuntimePath, std::string *psConfigPath, std::string *psLogPath, const char *pchConfigPathOverride, const char *pchLogPathOverride, std::vector<std::string> *pvecExternalDrivers )
334 {
335        CVRPathRegistry_Public pathReg;
336        bool bLoadedRegistry = pathReg.BLoadFromFile();
337        int nCountEnvironmentVariables = 0;
338
339        if( psRuntimePath )
340        {
341               if ( GetEnvironmentVariable( k_pchRuntimeOverrideVar ).length() != 0 )
342               {
343                      *psRuntimePath = GetEnvironmentVariable( k_pchRuntimeOverrideVar );
344                      nCountEnvironmentVariables++;
345               }
346               else if( !pathReg.GetRuntimePath().empty() )
347               {
348                      *psRuntimePath = pathReg.GetRuntimePath();
349               }
350               else
351               {
352                      *psRuntimePath = "";
353               }
354        }
355
356        if( psConfigPath )
357        {
358               if ( GetEnvironmentVariable( k_pchConfigOverrideVar ).length() != 0 )
359               {
360                      *psConfigPath = GetEnvironmentVariable( k_pchConfigOverrideVar );
361                      nCountEnvironmentVariables++;
362               }
363               else if( pchConfigPathOverride )
364               {
365                      *psConfigPath = pchConfigPathOverride;
366               }
367               else if( !pathReg.GetConfigPath().empty() )
368               {
369                      *psConfigPath = pathReg.GetConfigPath();
370               }
371               else
372               {
373                      *psConfigPath = "";
374               }
375        }
376
377        if( psLogPath )
378        {
379               if ( GetEnvironmentVariable( k_pchLogOverrideVar ).length() != 0 )
380               {
381                      *psLogPath = GetEnvironmentVariable( k_pchLogOverrideVar );
382                      nCountEnvironmentVariables++;
383               }
384               else if( pchLogPathOverride )
385               {
386                      *psLogPath = pchLogPathOverride;
387               }
388               else if( !pathReg.GetLogPath().empty() )
389               {
390                      *psLogPath = pathReg.GetLogPath();
391               }
392               else
393               {
394                      *psLogPath = "";
395               }
396        }
397
398        if ( pvecExternalDrivers )
399        {
400               *pvecExternalDrivers = pathReg.m_vecExternalDrivers;
401        }
402
403        if ( nCountEnvironmentVariables == 3 )
404        {
405               // all three environment variables where set, so we don't need the physical file
406               return true;
407        }
408
409        return bLoadedRegistry;
410 }
411