Merge remote-tracking branch 'remotes/origin/cairocanvas' into windows
[ardour.git] / libs / pbd / fallback_folders.cc
1 /*
2     Copyright (C) 2008 John Emmas
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <pbd/fallback_folders.h>
21 #include <glib.h>
22 #include <glibmm.h>
23 #include <string.h>
24
25
26
27 #ifdef PLATFORM_WINDOWS // Would not be relevant for Cygwin!!
28 #include <shlobj.h>
29 #include <winreg.h>
30
31 //***************************************************************
32 //
33 //      get_win_special_folder()
34 //
35 //  Gets the full path name that corresponds of one of the Windows
36 //  special folders, such as "My Documents" and the like. The input
37 //  parameter must be one of the corresponding CSIDL values, such
38 //  as CSIDL_SYSTEM etc.
39 //  
40 //      Returns:
41 //
42 //    On Success: A pointer to a newly allocated string containing
43 //                the name of the special folder (must later be freed).
44 //    On Failure: NULL
45 //
46 gchar *
47 get_win_special_folder (int csidl)
48 {
49 wchar_t path[PATH_MAX+1];
50 HRESULT hr;
51 LPITEMIDLIST pidl = 0;
52 gchar *retval = 0;
53
54         if (S_OK == (hr = SHGetSpecialFolderLocation (0, csidl, &pidl)))
55         {
56                 if (SHGetPathFromIDListW (pidl, path))
57                         retval = g_utf16_to_utf8 ((const gunichar2*)path, -1, 0, 0, 0);
58                 CoTaskMemFree (pidl);
59         }
60
61         return retval;
62 }
63 #endif // PLATFORM_WINDOWS
64
65 namespace PBD {
66
67 static  gchar **fallback_folders = 0;
68
69 //***************************************************************
70 //
71 //      get_platform_fallback_folders()
72 //
73 //  Returns an array of folders to fall back to if the folders
74 //  weren't named at build time and subsequently couldn't be found
75 //  in the user's environment. This might not be needed any more
76 //  because the function 'fixup_bundle_environment()' (in the
77 //  gtk2_ardour branch) now explicitly sets up any environment
78 //  paths that the program will need at run time. However, having
79 //  the folders here might help us to simplify the above function
80 //  which would be useful (currently, there are different versions
81 //  of 'fixup_bundle_environment()' for each supported platform).
82 //  Twelve fallback folders are currently catered for, corresponding to:-
83 //
84 //      LOCALEDIR
85 //      GTK_DIR
86 //      CONFIG_DIR
87 //      ARDOUR_DIR
88 //      MODULE_DIR
89 //      DATA_DIR
90 //      ICONS_DIR
91 //      PIXMAPS_DIR
92 //      CONTROL_SURFACES_DIR
93 //      VAMP_DIR
94 //      LADSPA_PATH - note that there's only one entry in the path
95 //      VST_PATH - note that there may only be one entry in the path
96 //
97 //      Returns:
98 //
99 //    On Success: A pointer to an array containing the above dirs.
100 //    On Failure: NULL
101 //
102 #ifdef PLATFORM_WINDOWS // Would not be relevant for Cygwin!!
103
104 static gchar**
105 get_platform_fallback_folders ()
106 {
107 gchar **fallback_dir_vector = 0;
108 const   gchar  *pUsrHome    = 0; // Do not free !!
109
110         if (!fallback_folders)
111         {
112                 GArray *pFallbackDirs;
113                 gchar *pAppData   = 0;
114                 gchar *pMyAppData = 0;
115                 gchar *pExeRoot   = 0;
116                 gchar *pPersonal  = 0;
117
118                 pFallbackDirs = g_array_new (TRUE, TRUE, sizeof (char *));
119
120                 if (pFallbackDirs)
121                 {
122                         /* Get the path for the user's personal folder */
123                         gchar *pPersonalTemp = get_win_special_folder (CSIDL_PERSONAL);
124
125                         /* and the path for the user's personal application data */
126                         gchar *pMyAppDataTemp = get_win_special_folder (CSIDL_LOCAL_APPDATA);
127
128                         /* and the path for common application data ("Documents and Settings\All Users\Application Data") */
129                         gchar *pAppDataTemp = get_win_special_folder (CSIDL_COMMON_APPDATA);
130
131                         if (0 == pAppDataTemp)
132                                 pAppData = g_build_filename("C:\\", "Documents and Settings", "All Users", "Application Data", PROGRAM_NAME, "local", 0);
133                         else
134                         {
135                                 pAppData = g_build_filename(pAppDataTemp, PROGRAM_NAME, "local", 0);
136                                 g_free (pAppDataTemp);
137                         }
138
139                         if (0 == pMyAppDataTemp)
140                         {
141                                 pMyAppData = g_build_filename(g_get_home_dir(), "Application Data", "local", 0);
142                         }
143                         else
144                         {
145                                 pMyAppData = g_build_filename(pMyAppDataTemp, 0);
146                                 g_free (pMyAppDataTemp);
147                         }
148
149                         if (0 == pPersonalTemp)
150                                 pPersonal = g_build_filename(g_get_home_dir(), 0);
151                         else
152                         {
153                                 pPersonal = g_build_filename(pPersonalTemp, 0);
154                                 g_free (pPersonalTemp);
155                         }
156
157                         /* Get the path to the running application */
158                         pExeRoot = g_win32_get_package_installation_directory_of_module (0);
159
160                         if (0 == pExeRoot)
161                         {
162                                 pExeRoot = g_build_filename("C:\\", "Program Files", PROGRAM_NAME, 0);
163                         }
164
165                         if ((pExeRoot) && (pAppData) && (pMyAppData) && (pPersonal))
166                         {
167                                 gchar  tmp[PATH_MAX+1];
168                                 gchar* p;
169
170                                 // Build our LOCALEDIR entry
171                                 if (0 != (p = g_build_filename(pAppData, "share", "locale", 0)))
172                                 {
173                                         g_array_append_val (pFallbackDirs, p);
174
175                                         // Build our GTK_DIR entry
176                                         if (0 != (p = g_build_filename(pPersonal, ".gtk-2.0", 0)))
177                                         {
178                                                 g_array_append_val (pFallbackDirs, p);
179
180                                                 // Build our CONFIG_DIR entry
181                                                 if (0 != (p = g_build_filename(pAppData, "etc", 0)))
182                                                 {
183                                                         g_array_append_val (pFallbackDirs, p);
184
185                                                         // Build our ARDOUR_DIR entry
186                                                         p = g_build_filename(pMyAppData, PROGRAM_NAME, 0);
187
188                                                         if (0 != p)
189                                                         {
190                                                                 g_array_append_val (pFallbackDirs, p);
191
192                                                                 // Build our MODULE_DIR entry
193                                                                 strcpy(tmp, pExeRoot);
194                                                                 if (0 != (p = strrchr (tmp, G_DIR_SEPARATOR)))
195                                                                 {
196                                                                         *p = '\0';
197
198                                                                         if (0 != (p = g_build_filename(tmp, 0)))
199                                                                         {
200                                                                                 g_array_append_val (pFallbackDirs, p);
201
202                                                                                 // Build our DATA_DIR entry
203                                                                                 if (0 != (p = g_build_filename(pAppData, "share", 0)))
204                                                                                 {
205                                                                                         g_array_append_val (pFallbackDirs, p);
206
207                                                                                         // Build our ICONS_DIR entry
208                                                                                         if (0 != (p = g_build_filename(pAppData, "share", "icons", 0)))
209                                                                                         {
210                                                                                                 g_array_append_val (pFallbackDirs, p);
211
212                                                                                                 // Build our PIXMAPS_DIR entry
213                                                                                                 if (0 != (p = g_build_filename(pAppData, "share", "pixmaps", 0)))
214                                                                                                 {
215                                                                                                         g_array_append_val (pFallbackDirs, p);
216
217                                                                                                         // Build our CONTROL_SURFACES_DIR entry
218                                                                                                         if (0 != (p = g_build_filename(pExeRoot, "bin", "surfaces", 0)))
219                                                                                                         {
220                                                                                                                 g_array_append_val (pFallbackDirs, p);
221
222                                                                                                                 // Build our VAMP_DIR entry
223                                                                                                                 p = g_build_filename(pExeRoot, "bin", "vamp", 0);
224                                                                                                                 if (p)
225                                                                                                                         g_array_append_val (pFallbackDirs, p);
226                                                                                                                 else
227                                                                                                                         g_array_append_val (pFallbackDirs, "");
228
229                                                                                                                 // Next, build our LADSPA_PATH entry
230                                                                                                                 p = g_build_filename(pExeRoot, "bin", "plugins", 0);
231                                                                                                                 if (p)
232                                                                                                                         g_array_append_val (pFallbackDirs, p);
233                                                                                                                 else
234                                                                                                                         g_array_append_val (pFallbackDirs, "");
235
236                                                                                                                 // And finally, build our VST_PATH entry
237                                                                                                                 DWORD dwType = REG_SZ;  HKEY hKey;
238                                                                                                                 DWORD dwSize = PATH_MAX;  p = 0;
239                                                                                                                 if (ERROR_SUCCESS == RegOpenKeyExA (HKEY_CURRENT_USER, "Software\\VST", 0, KEY_READ, &hKey))
240                                                                                                                 {
241                                                                                                                         // Look for the user's VST Registry entry
242                                                                                                                         if (ERROR_SUCCESS == RegQueryValueExA (hKey, "VSTPluginsPath", 0, &dwType, (LPBYTE)tmp, &dwSize))
243                                                                                                                                 p = g_build_filename (Glib::locale_to_utf8(tmp).c_str(), 0);
244
245                                                                                                                         RegCloseKey (hKey);
246                                                                                                                 }
247
248                                                                                                                 if (p == 0)
249                                                                                                                         if (ERROR_SUCCESS == RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\VST", 0, KEY_READ, &hKey))
250                                                                                                                         {
251                                                                                                                                 // Look for a global VST Registry entry
252                                                                                                                                 if (ERROR_SUCCESS == RegQueryValueExA (hKey, "VSTPluginsPath", 0, &dwType, (LPBYTE)tmp, &dwSize))
253                                                                                                                                         p = g_build_filename (Glib::locale_to_utf8(tmp).c_str(), 0);
254
255                                                                                                                                 RegCloseKey (hKey);
256                                                                                                                         }
257
258                                                                                                                 if (p == 0)
259                                                                                                                 {
260                                                                                                                         gchar *pVSTx86 = 0;
261                                                                                                                         gchar *pProgFilesX86 = get_win_special_folder (CSIDL_PROGRAM_FILESX86);
262
263                                                                                                                         if (pProgFilesX86)
264                                                                                                                         {
265                                                                                                                                 // Look for a VST folder under C:\Program Files (x86)
266                                                                                                                                 if (pVSTx86 = g_build_filename (pProgFilesX86, "Steinberg", "VSTPlugins", 0))
267                                                                                                                                 {
268                                                                                                                                         if (Glib::file_test (pVSTx86, Glib::FILE_TEST_EXISTS))
269                                                                                                                                                 if (Glib::file_test (pVSTx86, Glib::FILE_TEST_IS_DIR))
270                                                                                                                                                         p = g_build_filename (pVSTx86, 0);
271
272                                                                                                                                         g_free (pVSTx86);
273                                                                                                                                 }
274
275                                                                                                                                 g_free (pProgFilesX86);
276                                                                                                                         }
277
278                                                                                                                         if (p == 0)
279                                                                                                                         {
280                                                                                                                                 // Look for a VST folder under C:\Program Files
281                                                                                                                                 gchar *pVST = 0;
282                                                                                                                                 gchar *pProgFiles = get_win_special_folder (CSIDL_PROGRAM_FILES);
283
284                                                                                                                                 if (pProgFiles)
285                                                                                                                                 {
286                                                                                                                                         if (pVST = g_build_filename (pProgFiles, "Steinberg", "VSTPlugins", 0))
287                                                                                                                                         {
288                                                                                                                                                 if (Glib::file_test (pVST, Glib::FILE_TEST_EXISTS))
289                                                                                                                                                         if (Glib::file_test (pVST, Glib::FILE_TEST_IS_DIR))
290                                                                                                                                                                 p = g_build_filename (pVST, 0);
291
292                                                                                                                                                 g_free (pVST);
293                                                                                                                                         }
294
295                                                                                                                                         g_free (pProgFiles);
296                                                                                                                                 }
297                                                                                                                         }
298                                                                                                                 }
299
300                                                                                                                 if (p == 0)
301                                                                                                                 {
302                                                                                                                         // If all else failed, assume the plugins are under "My Documents"
303                                                                                                                         pUsrHome = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
304                                                                                                                         if (pUsrHome)
305                                                                                                                                 p = g_build_filename (pUsrHome, "Plugins", "VST", 0);
306                                                                                                                         else
307                                                                                                                         {
308                                                                                                                                 pUsrHome = g_build_filename(g_get_home_dir(), "My Documents", 0);
309                                                                                                                                 if (pUsrHome)
310                                                                                                                                         p = g_build_filename (pUsrHome, "Plugins", "VST", 0);
311                                                                                                                         }
312                                                                                                                 }
313                                                                                                                 else
314                                                                                                                 {
315                                                                                                                         gchar* q = 0;
316
317                                                                                                                         // Concatenate the registry path with the user's personal path
318                                                                                                                         pUsrHome = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
319
320                                                                                                                         if (pUsrHome)
321                                                                                                                         {
322                                                                                                                                 q = p;
323                                                                                                                                 p = g_build_path (";", q, g_build_filename(pUsrHome, "Plugins", "VST", 0), 0);
324                                                                                                                         }
325                                                                                                                         else
326                                                                                                                         {
327                                                                                                                                 pUsrHome = g_build_filename(g_get_home_dir(), "My Documents", 0);
328                                                                                                                                 if (pUsrHome)
329                                                                                                                                 {
330                                                                                                                                         q = p;
331                                                                                                                                         p = g_build_path (";", q, g_build_filename (pUsrHome, "Plugins", "VST", 0), 0);
332                                                                                                                                 }
333                                                                                                                         }
334                                                                                                                 }
335
336                                                                                                                 if (p) //VST
337                                                                                                                         g_array_append_val (pFallbackDirs, p);
338                                                                                                                 else
339                                                                                                                         g_array_append_val (pFallbackDirs, "");
340
341                                                                                                                 // BUNDLED_LV2
342                                                                                                                 p = g_build_filename(pExeRoot, "bin", "lv2", 0);
343                                                                                                                 if (p)
344                                                                                                                         g_array_append_val (pFallbackDirs, p);
345                                                                                                                 else
346                                                                                                                         g_array_append_val (pFallbackDirs, "");
347                                                                                                         }
348                                                                                                 }
349                                                                                         }
350                                                                                 }
351                                                                         }
352                                                                 }
353                                                         }
354                                                 }
355                                         }
356                                 }
357                         
358                                 g_free (pAppData);
359                                 g_free (pMyAppData);
360                                 g_free (pExeRoot);
361                                 g_free (pPersonal);
362                         }
363
364                         fallback_dir_vector = fallback_folders = (gchar **) g_array_free (pFallbackDirs, FALSE);
365                 }
366         }
367         else
368                 fallback_dir_vector = fallback_folders;
369
370         return (fallback_dir_vector);
371 }
372
373 #else
374 // Assume Linux, Cygwin or OS-X. Note that in all 3 cases we only
375 // need to cater for unbundled releases (those built by a user from
376 // source). Bundled releases of Ardour and Mixbus now specifically
377 // write their folders and paths to the user's environment at startup.
378 // See the function 'fixup_bundle_environment()'.
379
380 static gchar**
381 get_platform_fallback_folders ()
382 {
383 gchar **fallback_dir_vector = 0;
384 gchar  *pUsrHome            = 0;
385
386         if (!fallback_folders)
387         {
388                 GArray *pFallbackDirs;
389                 gchar *pAppData  = 0;
390                 gchar *pExeRoot  = 0;
391                 gchar *pPersonal = 0;
392
393                 pFallbackDirs = g_array_new (TRUE, TRUE, sizeof (char *));
394
395                 if (pFallbackDirs)
396                 {
397                         pAppData  = g_build_filename("/usr", "local", 0);
398                         pExeRoot  = g_build_filename("/usr", "local", "lib", "ardour2", 0);
399                         pPersonal = g_build_filename(g_get_home_dir(), 0);
400
401                         if ((pExeRoot) && (pAppData) && (pPersonal))
402                         {
403                                 gchar  tmp[PATH_MAX+1];
404                                 gchar* p;
405
406                                 // Build our LOCALEDIR entry
407                                 if (0 != (p = g_build_filename(pAppData, "share", "locale", 0)))
408                                 {
409                                         g_array_append_val (pFallbackDirs, p);
410
411                                         // Build our GTK_DIR entry
412                                         if (0 != (p = g_build_filename(pPersonal, ".gtk-2.0", 0)))
413                                         {
414                                                 g_array_append_val (pFallbackDirs, p);
415
416                                                 // Build our CONFIG_DIR entry
417                                                 if (0 != (p = g_build_filename(pAppData, "etc", 0)))
418                                                 {
419                                                         g_array_append_val (pFallbackDirs, p);
420
421                                                         // Build our ARDOUR_DIR entry
422                                                         p = ""; // Empty string (temporary)
423                                                         if (0 != p)
424                                                         {
425                                                                 g_array_append_val (pFallbackDirs, p);
426
427                                                                 // Build our MODULE_DIR entry
428                                                                 strcpy(tmp, pExeRoot);
429                                                                 if (0 != (p = strrchr (tmp, G_DIR_SEPARATOR)))
430                                                                 {
431                                                                         *p = '\0';
432
433                                                                         if (0 != (p = g_build_filename(tmp, 0)))
434                                                                         {
435                                                                                 g_array_append_val (pFallbackDirs, p);
436
437                                                                                 // Build our DATA_DIR entry
438                                                                                 if (0 != (p = g_build_filename(pAppData, "share", 0)))
439                                                                                 {
440                                                                                         g_array_append_val (pFallbackDirs, p);
441
442                                                                                         // Build our ICONS_DIR entry (re-use 'tmp')
443                                                                                         strcpy(tmp, "/usr/local/share/ardour2");
444                                                                                         if (0 != (p = g_build_filename(tmp, "icons", 0)))
445                                                                                         {
446                                                                                                 g_array_append_val (pFallbackDirs, p);
447
448                                                                                                 // Build our PIXMAPS_DIR entry
449                                                                                                 if (0 != (p = g_build_filename(tmp, "pixmaps", 0)))
450                                                                                                 {
451                                                                                                         g_array_append_val (pFallbackDirs, p);
452
453                                                                                                         // Build our CONTROL_SURFACES_DIR entry
454                                                                                                         if (0 != (p = g_build_filename(pExeRoot, "surfaces", 0)))
455                                                                                                         {
456                                                                                                                 g_array_append_val (pFallbackDirs, p);
457
458                                                                                                                 // Build our VAMP_DIR entry
459                                                                                                                 p = g_build_filename(pExeRoot, "vamp", 0);
460                                                                                                                 if (p)
461                                                                                                                         g_array_append_val (pFallbackDirs, p);
462
463                                                                                                                 // Next, build our LADSPA_PATH entry
464                                                                                                                 p = g_build_filename(Glib::path_get_dirname(pExeRoot).c_str(), "plugins", 0);
465                                                                                                                 if (p)
466                                                                                                                         g_array_append_val (pFallbackDirs, p);
467
468                                                                                                                 // And finally, build our VST_PATH entry
469                                                                                                                 if (g_getenv("HOME"))
470                                                                                                                         p = g_build_filename(g_getenv("HOME"), "VST", "plugins", 0);
471                                                                                                                 else
472                                                                                                                         p = g_build_filename(g_get_home_dir(), "VST", "plugins", 0);
473
474                                                                                                                 if (p)
475                                                                                                                         g_array_append_val (pFallbackDirs, p);
476                                                                                                         }
477                                                                                                 }
478                                                                                         }
479                                                                                 }
480                                                                         }
481                                                                 }
482                                                         }
483                                                 }
484                                         }
485                                 }
486                         
487                                 g_free (pAppData);
488                                 g_free (pExeRoot);
489                                 g_free (pPersonal);
490                         }
491
492                         fallback_dir_vector = fallback_folders = (gchar **) g_array_free (pFallbackDirs, FALSE);
493                 }
494         }
495         else
496                 fallback_dir_vector = fallback_folders;
497
498         if (pUsrHome)
499                 g_free (pUsrHome);
500
501         return (fallback_dir_vector);
502 }
503 #endif
504
505
506 //***************************************************************
507 //
508 //      get_platform_fallback_folder()
509 //
510 //  Returns a const gchar* which points to a string describing
511 //  the full path to the Ardour fallback folder corresponding to
512 //  the supplied index. See 'get_platform_fallback_folders()' for a
513 //  complete list of the supported index enumerations. Calling this
514 //  function will initialize the fallback folder array if it wasn't
515 //  already initiaized. The array should then (eventually) be freed
516 //  using 'free_platform_fallback_folders()'.
517 //
518 //      Returns:
519 //
520 //    On Success: A pointer to the path string contained at the
521 //                relevant index.
522 //    On Failure: NULL
523 //
524 PBD_API G_CONST_RETURN gchar* PBD_APICALLTYPE
525 get_platform_fallback_folder (PBD::fallback_folder_t index)
526 {
527         if ((index >= 0) && (index < FALLBACK_FOLDER_MAX))
528                 return ((G_CONST_RETURN gchar *)get_platform_fallback_folders ()[index]);
529         else
530                 return (G_CONST_RETURN gchar *) 0;
531 }
532
533
534 //***************************************************************
535 //
536 //      alloc_platform_fallback_folders()
537 //
538 //  Calls 'get_platform_fallback_folders()' to ensure that memory
539 //  for the fallback folder array is already allocated before the
540 //  array gets used. It doesn't cause any problems if the array gets
541 //  used prior to calling this function (since the memory will get
542 //  allocated anyway, on fist usage). Either way however, the momory
543 //  must later be freed using 'free_platform_fallback_folders()'.
544 //
545 //      Returns:
546 //
547 //    The value obtained from 'get_platform_fallback_folders()'
548 //
549 PBD_API G_CONST_RETURN gchar* G_CONST_RETURN * PBD_APICALLTYPE
550 alloc_platform_fallback_folders ()
551 {
552         return ((G_CONST_RETURN gchar* G_CONST_RETURN *)get_platform_fallback_folders ());
553 }
554
555
556 //***************************************************************
557 //
558 //      free_platform_fallback_folders()
559 //
560 //  Frees the memory that was previously allocated for the Ardour
561 //  fallback folder array.
562 //
563 //      Returns:
564 //
565 //    NONE.
566 //
567 PBD_API void PBD_APICALLTYPE
568 free_platform_fallback_folders ()
569 {
570 int index = FOLDER_LOCALE;
571
572         if (fallback_folders)
573         {
574                 gchar *p = get_platform_fallback_folders()[(fallback_folder_t)index++];
575
576                 while (index < (FALLBACK_FOLDER_MAX+1)) {
577                         if (p)
578                                 g_free (p);
579
580                         if (index < FALLBACK_FOLDER_MAX)
581                                 p = get_platform_fallback_folders()[(fallback_folder_t)index++];
582                         else
583                                 break;
584                 }
585
586                 fallback_folders = 0;
587         }
588 }
589
590 }  // namespace PBD
591