replace ::cast_dynamic() with relevant ActionManager::get_*_action() calls
[ardour.git] / gtk2_ardour / bundle_env_msvc.cc
1 /*
2     Copyright (C) 2014 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 "bundle_env.h"
21 #include "pbd/i18n.h"
22
23 #include <shlobj.h>
24 #include <stdlib.h>
25
26 #include <iostream>
27 #include <string>
28 #include <vector>
29 #include <fstream>
30
31 #include <glibmm.h>
32 #include "pbd/gstdio_compat.h"
33
34 #include <fontconfig/fontconfig.h>
35
36 #include "ardour/ardour.h"
37 #include "ardour/search_paths.h"
38 #include "ardour/filesystem_paths.h"
39
40 #include "pbd/file_utils.h"
41 #include "pbd/epa.h"
42
43 using namespace std;
44 using namespace PBD;
45 using namespace ARDOUR;
46
47 std::string
48 get_windows_drive_volume_letter()
49 {
50 static std::string ret;
51 char path[PATH_MAX+1];
52 LPITEMIDLIST pidl = 0;
53
54         if (!ret.length()) {
55                 if (S_OK == SHGetSpecialFolderLocation (0, CSIDL_WINDOWS, &pidl))
56                 {
57                         if (SHGetPathFromIDListA (pidl, path)) {
58                                 path[2] = '\0'; // Gives us just the drive letter and colon
59                                 ret = path;
60                         }
61
62                         CoTaskMemFree (pidl);
63                 }
64                 // The above should never fail - but just in case...
65                 else if (char *env_path = getenv ("windir"))
66                 {
67                         strcpy (path, env_path);
68                         path[2] = '\0'; // Gives us just the drive letter and colon
69                         ret = path;
70                 }
71         }
72
73         return ret;
74 }
75
76 const string
77 get_module_folder ()
78 {
79 std::string ret;
80
81         // Gives the top-level Ardour installation folder (on Windows)
82         // Typically, this will be somehwere like "C:\Program Files"
83
84         gchar* pExeRoot = g_win32_get_package_installation_directory_of_module (0);
85
86         if (0 == pExeRoot) {
87                 pExeRoot = g_build_filename("C:\\", "Program Files", PROGRAM_NAME, 0);
88         }
89
90         if (pExeRoot) {
91                 gchar  tmp[PATH_MAX+1];
92                 gchar* p;
93
94                 strcpy(tmp, pExeRoot);
95                 if (0 != (p = strrchr (tmp, G_DIR_SEPARATOR))) {
96                         *p = '\0';
97
98                         if (0 != (p = g_build_filename(tmp, 0))) {
99                                 ret = p;
100                                 g_free (p);
101                         }
102                 }
103
104                 g_free (pExeRoot);
105         }
106
107         return (ret);
108 }
109
110 bool
111 fixup_config_file (Glib::ustring str_file_to_fix)
112 {
113 FILE* fd;
114 char  buf[4096];
115 bool  conversion_needed = false;
116 bool  succeeded = false;
117
118         fstream file_to_fix (fd = g_fopen(str_file_to_fix.c_str(), "r+b"));
119
120         if (file_to_fix.is_open()) {
121                 vector<std::string> lines;
122                 std::string line;
123
124                 file_to_fix.seekg (0, std::ios::beg);
125                 file_to_fix.seekp (0, std::ios::beg);
126
127                 try {
128                         while (!file_to_fix.eof() && file_to_fix.getline (buf, sizeof(buf))) {
129                                 line = buf;
130
131                                 if (!conversion_needed && (std::string::npos != line.find("$(")))
132                                         conversion_needed = true;
133                                 lines.push_back(line);
134                         }
135
136                         if (conversion_needed) {
137                                 bool error = false;
138                                 std::string::size_type token_begin, token_end;
139                                 vector<string>::iterator i;
140
141                                 for (i = lines.begin(); i != lines.end(); ++i) {
142                                         if (string::npos != (token_begin = i->find("$("))) {
143                                                 if (string::npos != (token_end = i->find(")", token_begin))) {
144                                                         std::string str_replace_with;
145                                                         std::string str_to_replace = i->substr(token_begin, ((token_end+1)-token_begin));
146
147                                                         if (0 == str_to_replace.compare("$(CWD)")) {
148                                                                 // Replace our token with the current working directory
149                                                                 if (getcwd(buf, sizeof(buf))) {
150                                                                         if (buf[strlen(buf)-1] == G_DIR_SEPARATOR)
151                                                                                 buf[strlen(buf)-1] = '\0';
152                                                                         str_replace_with = buf;
153
154                                                                         // Replace the first occurrence of our token with the required string
155                                                                         i->erase(token_begin, ((token_end+1)-token_begin));
156                                                                         i->insert(token_begin, str_replace_with);
157                                                                 } else {
158                                                                         error = true;
159                                                                 }
160                                                         } else if (0 == str_to_replace.compare("$(WINDRIVE)")){
161                                                                 // Replace our token with the drive letter (and colon) for the user's Windows volume
162                                                                 str_replace_with = get_windows_drive_volume_letter();
163
164                                                                 // Replace the first occurrence of our token with the required string
165                                                                 i->erase(token_begin, ((token_end+1)-token_begin));
166                                                                 i->insert(token_begin, str_replace_with);
167                                                         } else if (0 == str_to_replace.compare("$(LOCALCACHEDIR)")){
168                                                                 // Replace our token with the path to our Ardour cache directory
169                                                                 str_replace_with = user_cache_directory();
170
171                                                                 // Replace the first occurrence of our token with the required string
172                                                                 i->erase(token_begin, ((token_end+1)-token_begin));
173                                                                 i->insert(token_begin, str_replace_with);
174                                                         } else {
175                                                                 // Assume that our token represents an environment variable
176                                                                 std::string envvar_name = str_to_replace.substr(2, str_to_replace.length()-3);
177
178                                                                 if (const char *envvar_value = getenv(envvar_name.c_str())) {
179                                                                         strcpy(buf, envvar_value);
180                                                                         if (buf[strlen(buf)-1] == G_DIR_SEPARATOR)
181                                                                                 buf[strlen(buf)-1] = '\0';
182                                                                         str_replace_with = buf;
183
184                                                                         // Replace the first occurrence of our token with the required string
185                                                                         i->erase(token_begin, ((token_end+1)-token_begin));
186                                                                         i->insert(token_begin, str_replace_with);
187                                                                 } else {
188                                                                         error = true;
189                                                                         cerr << _("ERROR: unknown environment variable") << endl;
190                                                                 }
191                                                         }
192                                                 }
193                                         }
194                                 }
195
196                                 if (!error) {
197                                         file_to_fix.clear ();                  // Clear the EOF flag etc
198                                         file_to_fix.seekg (0, std::ios::beg);  // Seek our 'get' ptr to the file start pos
199                                                                                                                    // (our 'put' ptr shouldn't have moved yet).
200                                         chsize(fileno (fd), 0);                // Truncate the file, ready for re-writing
201
202                                         for (i = lines.begin(); i != lines.end(); ++i) {
203
204                                                 // Write the converted contents to our file
205                                                 file_to_fix << (*i).c_str() << endl;
206                                         }
207
208                                         try {
209                                                 file_to_fix.close();
210                                                 succeeded = true;
211                                         } catch (...) {}
212                                 }
213                         } else {
214                                 file_to_fix.close();
215                                 succeeded = true;
216                         }
217                 } catch (...) {
218                         file_to_fix.close();
219                         succeeded = false;
220                 }
221         } else {
222                 cerr << _("ERROR: Could not open config file '") << str_file_to_fix << "'" << endl;
223         }
224
225         return succeeded;
226 }
227
228 void
229 fixup_fonts_config ()
230 {
231 string fonts_conf_file;
232
233 #ifdef DEBUG
234         fonts_conf_file = get_module_folder();
235
236         if (!fonts_conf_file.empty()) {
237                 fonts_conf_file += "\\";
238                 fonts_conf_file += PROGRAM_NAME;
239                 fonts_conf_file += PROGRAM_VERSION;
240                 fonts_conf_file += FONTS_CONF_LOCATION;
241 #else
242         if (PBD::find_file (ARDOUR::ardour_config_search_path(), "fonts.conf", fonts_conf_file)) {
243 #endif
244                 Glib::setenv ("FONTCONFIG_FILE", fonts_conf_file, true);
245
246                 if (0 == fixup_config_file (fonts_conf_file))
247                         cerr << _("ERROR: processing error for 'fonts.conf' file") << endl;
248         } else {
249                 cerr << _("ERROR: Malformed module folder (fonts.conf)") << endl;
250         }
251 }
252
253 void
254 fixup_pango_config ()
255 {
256 string pango_modules_file;
257
258 #if defined(DEBUG) || defined(RDC_BUILD)
259         // Make sure we pick up the debuggable DLLs !!!
260         pango_modules_file = get_module_folder();
261
262         if (!pango_modules_file.empty()) {
263                 pango_modules_file += "\\";
264                 pango_modules_file += PROGRAM_NAME;
265                 pango_modules_file += PROGRAM_VERSION;
266                 pango_modules_file += PANGO_CONF_LOCATION;
267 #if 0
268 // JE - handy for non-English locale testing (Greek, in this case)
269                 Glib::ustring pango_modules_path = Glib::locale_to_utf8("C:\\Program Files\\Mixbus3\\etc\\��������\\pango.modules");
270 /**/
271 #else
272                 Glib::ustring pango_modules_path = pango_modules_file;
273 #endif
274                 pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end
275 #else
276         if (PBD::find_file (ARDOUR::ardour_config_search_path(), "pango.modules", pango_modules_file)) {
277
278                 Glib::ustring pango_modules_path = pango_modules_file;
279                 pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end
280 #endif
281                 // Set an environment variable so we can find our pango modules. Note
282                 // that this requires a modified version of libpango (pango-utils.c)
283                 Glib::setenv ("PANGO_MODULE_PATH", Glib::filename_from_utf8(pango_modules_path), true);
284
285                 if (0 == fixup_config_file (pango_modules_file))
286                         cerr << _("ERROR: processing error for 'pango.modules' file") << endl;
287         } else {
288                 cerr << _("ERROR: Malformed module folder (pango.modules)") << endl;
289         }
290 }
291
292 void
293 fixup_pixbuf_loaders_config ()
294 {
295 string gdk_pixbuf_loaders_file;
296
297 #if defined(DEBUG) || defined(RDC_BUILD)
298         // Make sure we pick up the debuggable DLLs !!!
299         gdk_pixbuf_loaders_file = get_module_folder();
300
301         if (!gdk_pixbuf_loaders_file.empty()) {
302                 gdk_pixbuf_loaders_file += "\\";
303                 gdk_pixbuf_loaders_file += PROGRAM_NAME;
304                 gdk_pixbuf_loaders_file += PROGRAM_VERSION;
305                 gdk_pixbuf_loaders_file += PIXBUFLOADERS_CONF_LOCATION;
306 #else
307         if (PBD::find_file (ARDOUR::ardour_config_search_path(), "gdk-pixbuf.loaders", gdk_pixbuf_loaders_file)) {
308 #endif
309                 // Set an environment variable so we can find our pixbuf modules.
310                 Glib::setenv ("GDK_PIXBUF_MODULE_FILE", Glib::filename_from_utf8(gdk_pixbuf_loaders_file), true);
311
312                 if (0 == fixup_config_file (gdk_pixbuf_loaders_file))
313                         cerr << _("ERROR: processing error for 'gdk-pixbuf.loaders' file") << endl;
314         } else {
315                 cerr << _("ERROR: Malformed module folder (gdk-pixbuf.loaders)") << endl;
316         }
317 }
318
319 void
320 fixup_clearlooks_config ()
321 {
322 string clearlooks_la_file;
323
324 #if defined(DEBUG) || defined(RDC_BUILD)
325         // Make sure we pick up the debuggable DLLs !!!
326         clearlooks_la_file = get_module_folder();
327
328         if (!clearlooks_la_file.empty()) {
329                 clearlooks_la_file += "\\";
330                 clearlooks_la_file += PROGRAM_NAME;
331                 clearlooks_la_file += PROGRAM_VERSION;
332                 clearlooks_la_file += CLEARLOOKS_CONF_LOCATION;
333 #else
334         if (PBD::find_file (ARDOUR::ardour_config_search_path(), "libclearlooks.la", clearlooks_la_file)) {
335 #endif
336                 // Set an environment variable so we can find our clearlooks engine.
337                 // Note that this requires a modified version of libgtk (gtkthemes.c)
338                 Glib::setenv ("GTK_THEME_ENGINE_FILE", Glib::filename_from_utf8(clearlooks_la_file).c_str(), true);
339
340                 if (0 == fixup_config_file (clearlooks_la_file))
341                         cerr << _("ERROR: processing error for 'clearlooks.la' file") << endl;
342         } else {
343                 cerr << _("ERROR: Malformed module folder (clearlooks.la)") << endl;
344         }
345 }
346
347 void
348 fixup_bundle_environment (int argc, char* argv[], string & localedir)
349 {
350         std::string exec_path = argv[0];
351         std::string dir_path  = Glib::path_get_dirname (exec_path);
352
353         // Make sure that our runtime CWD is set to Mixbus's install
354         // folder, regardless of where the caller's CWD was set to.
355         g_chdir (dir_path.c_str());
356
357         EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true));
358
359         // Now set 'dir_path' so we can append some relative paths
360         dir_path = Glib::path_get_dirname (dir_path);
361
362         std::string path;
363         const  char *cstr;
364
365         // First, set up 'ARDOUR_DLL_PATH'
366         path  = dir_path;
367         path += "\\lib\\ardour3\\surfaces;";
368         path += dir_path;
369         path += "\\lib\\ardour3\\panners;";
370         path += dir_path;
371         path += "\\lib\\ardour3\\backends;";
372         path += dir_path;
373         path += "\\bin";
374         Glib::setenv ("ARDOUR_DLL_PATH", path, true);
375
376
377         // Next, set up 'ARDOUR_DATA_PATH'
378         path  = get_module_folder() + "\\";
379         path += PROGRAM_NAME;
380         path += PROGRAM_VERSION;
381         path += "\\share";
382         Glib::setenv ("ARDOUR_DATA_PATH", path, true);
383
384
385         // Next, set up 'ARDOUR_CONFIG_PATH'
386 #ifdef _WIN64
387         path = user_config_directory() + "\\win64;";
388 #else
389         path = user_config_directory() + "\\win32;";
390 #endif
391         Glib::setenv ("ARDOUR_CONFIG_PATH", path, true);
392
393
394         // Next, set up 'ARDOUR_INSTANT_XML_PATH'
395         path = user_config_directory();
396         Glib::setenv ("ARDOUR_INSTANT_XML_PATH", path, true);
397
398
399         // Next, set up 'LADSPA_PATH'
400         path = ladspa_search_path().to_string();
401         Glib::setenv ("LADSPA_PATH", path, true);
402
403
404         // Next, set up 'SUIL_MODULE_DIR'
405         Glib::setenv ("SUIL_MODULE_DIR", Glib::build_filename(ardour_dll_directory(), "suil"), true);
406
407
408         // Next, set up 'VAMP_PATH'
409         cstr = getenv ("VAMP_PATH");
410         if (cstr) {
411                 path = cstr;
412                 path += G_SEARCHPATH_SEPARATOR;
413         } else {
414                 path = "";
415         }
416         path += get_module_folder() + "\\";
417         path += PROGRAM_NAME;
418         path += PROGRAM_VERSION;
419         path += "\\bin\\vamp";
420         path += G_SEARCHPATH_SEPARATOR;
421         path += "%ProgramFiles%\\Vamp Plugins";
422         Glib::setenv ("VAMP_PATH", path, true);
423
424
425         // Next, set up 'ARDOUR_CONTROL_SURFACE_PATH'
426         cstr = getenv ("ARDOUR_CONTROL_SURFACE_PATH");
427         if (cstr) {
428                 path = cstr;
429                 path += G_SEARCHPATH_SEPARATOR;
430         } else {
431                 path = "";
432         }
433         path += control_protocol_search_path().to_string();
434         Glib::setenv ("ARDOUR_CONTROL_SURFACE_PATH", path, true);
435
436
437         // Next, set up 'GTK_LOCALEDIR'
438         if (ARDOUR::translations_are_enabled ()) {
439                 path = windows_search_path().to_string();
440                 path += "\\locale";
441                 Glib::setenv ("GTK_LOCALEDIR", path, true);
442
443                 // and return the same path to our caller
444                 localedir = path;
445         }
446
447
448         // Next, set up 'GTK_PATH'
449         cstr = getenv ("GTK_PATH");
450         if (cstr) {
451                 path = cstr;
452                 path += G_SEARCHPATH_SEPARATOR;
453         } else {
454                 path = "";
455         }
456         path += user_config_directory();
457         path += "\\.gtk-2.0";
458         Glib::setenv ("GTK_PATH", path, true);
459
460
461         // Unset GTK2_RC_FILES so that we only load the RC files that we define
462         Glib::unsetenv ("GTK2_RC_FILES");
463
464
465         // and set a '$HOME' environment variable. This variable changes the value returned
466         // by 'g_get_home_dir()' so to prevent that function from unexpectedly changing its
467         // mind, we'll set '$HOME' to whatever 'g_get_home_dir()' is already returning!!
468         if (NULL == getenv("HOME")) {
469                 Glib::setenv ("HOME", Glib::locale_from_utf8(g_get_home_dir()), true);
470         }
471
472         fixup_fonts_config();
473         fixup_clearlooks_config();
474
475 #ifdef DLL_PIXBUF_LOADERS
476         fixup_pixbuf_loaders_config();
477 #endif
478 #ifdef DLL_PANGO_MODULES
479         fixup_pango_config();
480 #endif
481 }
482
483
484 void load_custom_fonts()
485 {
486         std::string ardour_mono_file;
487
488         if (!find_file (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
489                 cerr << _("Cannot find ArdourMono TrueType font") << endl;
490         }
491
492         FcConfig *config = FcInitLoadConfigAndFonts();
493         FcBool ret = FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(ardour_mono_file.c_str()));
494
495         if (ret == FcFalse) {
496                 cerr << _("Cannot load ArdourMono TrueType font.") << endl;
497         }
498
499         ret = FcConfigSetCurrent(config);
500
501         if (ret == FcFalse) {
502                 cerr << _("Failed to set fontconfig configuration.") << endl;
503         }
504 }