2 * Copyright (C) 2014-2017 John Emmas <john@creativepost.co.uk>
3 * Copyright (C) 2015-2016 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2015-2016 Robin Gareus <robin@gareus.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "bundle_env.h"
33 #include "pbd/gstdio_compat.h"
35 #include <fontconfig/fontconfig.h>
37 #include "ardour/ardour.h"
38 #include "ardour/search_paths.h"
39 #include "ardour/filesystem_paths.h"
41 #include "pbd/file_utils.h"
46 using namespace ARDOUR;
49 get_windows_drive_volume_letter()
51 static std::string ret;
52 char path[PATH_MAX+1];
53 LPITEMIDLIST pidl = 0;
56 if (S_OK == SHGetSpecialFolderLocation (0, CSIDL_WINDOWS, &pidl))
58 if (SHGetPathFromIDListA (pidl, path)) {
59 path[2] = '\0'; // Gives us just the drive letter and colon
65 // The above should never fail - but just in case...
66 else if (char *env_path = getenv ("windir"))
68 strcpy (path, env_path);
69 path[2] = '\0'; // Gives us just the drive letter and colon
82 // Gives the top-level Ardour installation folder (on Windows)
83 // Typically, this will be somehwere like "C:\Program Files"
85 gchar* pExeRoot = g_win32_get_package_installation_directory_of_module (0);
88 pExeRoot = g_build_filename("C:\\", "Program Files", PROGRAM_NAME, 0);
92 gchar tmp[PATH_MAX+1];
95 strcpy(tmp, pExeRoot);
96 if (0 != (p = strrchr (tmp, G_DIR_SEPARATOR))) {
99 if (0 != (p = g_build_filename(tmp, 0))) {
112 fixup_config_file (Glib::ustring str_file_to_fix)
116 bool conversion_needed = false;
117 bool succeeded = false;
119 fstream file_to_fix (fd = g_fopen(str_file_to_fix.c_str(), "r+b"));
121 if (file_to_fix.is_open()) {
122 vector<std::string> lines;
125 file_to_fix.seekg (0, std::ios::beg);
126 file_to_fix.seekp (0, std::ios::beg);
129 while (!file_to_fix.eof() && file_to_fix.getline (buf, sizeof(buf))) {
132 if (!conversion_needed && (std::string::npos != line.find("$(")))
133 conversion_needed = true;
134 lines.push_back(line);
137 if (conversion_needed) {
139 std::string::size_type token_begin, token_end;
140 vector<string>::iterator i;
142 for (i = lines.begin(); i != lines.end(); ++i) {
143 if (string::npos != (token_begin = i->find("$("))) {
144 if (string::npos != (token_end = i->find(")", token_begin))) {
145 std::string str_replace_with;
146 std::string str_to_replace = i->substr(token_begin, ((token_end+1)-token_begin));
148 if (0 == str_to_replace.compare("$(CWD)")) {
149 // Replace our token with the current working directory
150 if (getcwd(buf, sizeof(buf))) {
151 if (buf[strlen(buf)-1] == G_DIR_SEPARATOR)
152 buf[strlen(buf)-1] = '\0';
153 str_replace_with = buf;
155 // Replace the first occurrence of our token with the required string
156 i->erase(token_begin, ((token_end+1)-token_begin));
157 i->insert(token_begin, str_replace_with);
161 } else if (0 == str_to_replace.compare("$(WINDRIVE)")){
162 // Replace our token with the drive letter (and colon) for the user's Windows volume
163 str_replace_with = get_windows_drive_volume_letter();
165 // Replace the first occurrence of our token with the required string
166 i->erase(token_begin, ((token_end+1)-token_begin));
167 i->insert(token_begin, str_replace_with);
168 } else if (0 == str_to_replace.compare("$(LOCALCACHEDIR)")){
169 // Replace our token with the path to our Ardour cache directory
170 str_replace_with = user_cache_directory();
172 // Replace the first occurrence of our token with the required string
173 i->erase(token_begin, ((token_end+1)-token_begin));
174 i->insert(token_begin, str_replace_with);
176 // Assume that our token represents an environment variable
177 std::string envvar_name = str_to_replace.substr(2, str_to_replace.length()-3);
179 if (const char *envvar_value = getenv(envvar_name.c_str())) {
180 strcpy(buf, envvar_value);
181 if (buf[strlen(buf)-1] == G_DIR_SEPARATOR)
182 buf[strlen(buf)-1] = '\0';
183 str_replace_with = buf;
185 // Replace the first occurrence of our token with the required string
186 i->erase(token_begin, ((token_end+1)-token_begin));
187 i->insert(token_begin, str_replace_with);
190 cerr << _("ERROR: unknown environment variable") << endl;
198 file_to_fix.clear (); // Clear the EOF flag etc
199 file_to_fix.seekg (0, std::ios::beg); // Seek our 'get' ptr to the file start pos
200 // (our 'put' ptr shouldn't have moved yet).
201 chsize(fileno (fd), 0); // Truncate the file, ready for re-writing
203 for (i = lines.begin(); i != lines.end(); ++i) {
205 // Write the converted contents to our file
206 file_to_fix << (*i).c_str() << endl;
223 cerr << _("ERROR: Could not open config file '") << str_file_to_fix << "'" << endl;
230 fixup_fonts_config ()
232 string fonts_conf_file;
235 fonts_conf_file = get_module_folder();
237 if (!fonts_conf_file.empty()) {
238 fonts_conf_file += "\\";
239 fonts_conf_file += PROGRAM_NAME;
240 fonts_conf_file += PROGRAM_VERSION;
241 fonts_conf_file += FONTS_CONF_LOCATION;
243 if (PBD::find_file (ARDOUR::ardour_config_search_path(), "fonts.conf", fonts_conf_file)) {
245 Glib::setenv ("FONTCONFIG_FILE", fonts_conf_file, true);
247 if (0 == fixup_config_file (fonts_conf_file))
248 cerr << _("ERROR: processing error for 'fonts.conf' file") << endl;
250 cerr << _("ERROR: Malformed module folder (fonts.conf)") << endl;
255 fixup_pango_config ()
257 string pango_modules_file;
259 #if defined(DEBUG) || defined(RDC_BUILD)
260 // Make sure we pick up the debuggable DLLs !!!
261 pango_modules_file = get_module_folder();
263 if (!pango_modules_file.empty()) {
264 pango_modules_file += "\\";
265 pango_modules_file += PROGRAM_NAME;
266 pango_modules_file += PROGRAM_VERSION;
267 pango_modules_file += PANGO_CONF_LOCATION;
269 // JE - handy for non-English locale testing (Greek, in this case)
270 Glib::ustring pango_modules_path = Glib::locale_to_utf8("C:\\Program Files\\Mixbus3\\etc\\��������\\pango.modules");
273 Glib::ustring pango_modules_path = pango_modules_file;
275 pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end
277 if (PBD::find_file (ARDOUR::ardour_config_search_path(), "pango.modules", pango_modules_file)) {
279 Glib::ustring pango_modules_path = pango_modules_file;
280 pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end
282 // Set an environment variable so we can find our pango modules. Note
283 // that this requires a modified version of libpango (pango-utils.c)
284 Glib::setenv ("PANGO_MODULE_PATH", Glib::filename_from_utf8(pango_modules_path), true);
286 if (0 == fixup_config_file (pango_modules_file))
287 cerr << _("ERROR: processing error for 'pango.modules' file") << endl;
289 cerr << _("ERROR: Malformed module folder (pango.modules)") << endl;
294 fixup_pixbuf_loaders_config ()
296 string gdk_pixbuf_loaders_file;
298 #if defined(DEBUG) || defined(RDC_BUILD)
299 // Make sure we pick up the debuggable DLLs !!!
300 gdk_pixbuf_loaders_file = get_module_folder();
302 if (!gdk_pixbuf_loaders_file.empty()) {
303 gdk_pixbuf_loaders_file += "\\";
304 gdk_pixbuf_loaders_file += PROGRAM_NAME;
305 gdk_pixbuf_loaders_file += PROGRAM_VERSION;
306 gdk_pixbuf_loaders_file += PIXBUFLOADERS_CONF_LOCATION;
308 if (PBD::find_file (ARDOUR::ardour_config_search_path(), "gdk-pixbuf.loaders", gdk_pixbuf_loaders_file)) {
310 // Set an environment variable so we can find our pixbuf modules.
311 Glib::setenv ("GDK_PIXBUF_MODULE_FILE", Glib::filename_from_utf8(gdk_pixbuf_loaders_file), true);
313 if (0 == fixup_config_file (gdk_pixbuf_loaders_file))
314 cerr << _("ERROR: processing error for 'gdk-pixbuf.loaders' file") << endl;
316 cerr << _("ERROR: Malformed module folder (gdk-pixbuf.loaders)") << endl;
321 fixup_clearlooks_config ()
323 string clearlooks_la_file;
325 #if defined(DEBUG) || defined(RDC_BUILD)
326 // Make sure we pick up the debuggable DLLs !!!
327 clearlooks_la_file = get_module_folder();
329 if (!clearlooks_la_file.empty()) {
330 clearlooks_la_file += "\\";
331 clearlooks_la_file += PROGRAM_NAME;
332 clearlooks_la_file += PROGRAM_VERSION;
333 clearlooks_la_file += CLEARLOOKS_CONF_LOCATION;
335 if (PBD::find_file (ARDOUR::ardour_config_search_path(), "libclearlooks.la", clearlooks_la_file)) {
337 // Set an environment variable so we can find our clearlooks engine.
338 // Note that this requires a modified version of libgtk (gtkthemes.c)
339 Glib::setenv ("GTK_THEME_ENGINE_FILE", Glib::filename_from_utf8(clearlooks_la_file).c_str(), true);
341 if (0 == fixup_config_file (clearlooks_la_file))
342 cerr << _("ERROR: processing error for 'clearlooks.la' file") << endl;
344 cerr << _("ERROR: Malformed module folder (clearlooks.la)") << endl;
349 fixup_bundle_environment (int argc, char* argv[], string & localedir)
351 std::string exec_path = argv[0];
352 std::string dir_path = Glib::path_get_dirname (exec_path);
354 // Make sure that our runtime CWD is set to Mixbus's install
355 // folder, regardless of where the caller's CWD was set to.
356 g_chdir (dir_path.c_str());
358 EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true));
360 // Now set 'dir_path' so we can append some relative paths
361 dir_path = Glib::path_get_dirname (dir_path);
366 // First, set up 'ARDOUR_DLL_PATH'
368 path += "\\lib\\ardour3\\surfaces;";
370 path += "\\lib\\ardour3\\panners;";
372 path += "\\lib\\ardour3\\backends;";
375 Glib::setenv ("ARDOUR_DLL_PATH", path, true);
378 // Next, set up 'ARDOUR_DATA_PATH'
379 path = get_module_folder() + "\\";
380 path += PROGRAM_NAME;
381 path += PROGRAM_VERSION;
383 Glib::setenv ("ARDOUR_DATA_PATH", path, true);
386 // Next, set up 'ARDOUR_CONFIG_PATH'
388 path = user_config_directory() + "\\win64;";
390 path = user_config_directory() + "\\win32;";
392 Glib::setenv ("ARDOUR_CONFIG_PATH", path, true);
395 // Next, set up 'ARDOUR_INSTANT_XML_PATH'
396 path = user_config_directory();
397 Glib::setenv ("ARDOUR_INSTANT_XML_PATH", path, true);
400 // Next, set up 'LADSPA_PATH'
401 path = ladspa_search_path().to_string();
402 Glib::setenv ("LADSPA_PATH", path, true);
405 // Next, set up 'SUIL_MODULE_DIR'
406 Glib::setenv ("SUIL_MODULE_DIR", Glib::build_filename(ardour_dll_directory(), "suil"), true);
409 // Next, set up 'VAMP_PATH'
410 cstr = getenv ("VAMP_PATH");
413 path += G_SEARCHPATH_SEPARATOR;
417 path += get_module_folder() + "\\";
418 path += PROGRAM_NAME;
419 path += PROGRAM_VERSION;
420 path += "\\bin\\vamp";
421 path += G_SEARCHPATH_SEPARATOR;
422 path += "%ProgramFiles%\\Vamp Plugins";
423 Glib::setenv ("VAMP_PATH", path, true);
426 // Next, set up 'ARDOUR_CONTROL_SURFACE_PATH'
427 cstr = getenv ("ARDOUR_CONTROL_SURFACE_PATH");
430 path += G_SEARCHPATH_SEPARATOR;
434 path += control_protocol_search_path().to_string();
435 Glib::setenv ("ARDOUR_CONTROL_SURFACE_PATH", path, true);
438 // Next, set up 'GTK_LOCALEDIR'
439 if (ARDOUR::translations_are_enabled ()) {
440 path = windows_search_path().to_string();
442 Glib::setenv ("GTK_LOCALEDIR", path, true);
444 // and return the same path to our caller
449 // Next, set up 'GTK_PATH'
450 cstr = getenv ("GTK_PATH");
453 path += G_SEARCHPATH_SEPARATOR;
457 path += user_config_directory();
458 path += "\\.gtk-2.0";
459 Glib::setenv ("GTK_PATH", path, true);
462 // Unset GTK2_RC_FILES so that we only load the RC files that we define
463 Glib::unsetenv ("GTK2_RC_FILES");
466 // and set a '$HOME' environment variable. This variable changes the value returned
467 // by 'g_get_home_dir()' so to prevent that function from unexpectedly changing its
468 // mind, we'll set '$HOME' to whatever 'g_get_home_dir()' is already returning!!
469 if (NULL == getenv("HOME")) {
470 Glib::setenv ("HOME", Glib::locale_from_utf8(g_get_home_dir()), true);
473 fixup_fonts_config();
474 fixup_clearlooks_config();
476 #ifdef DLL_PIXBUF_LOADERS
477 fixup_pixbuf_loaders_config();
479 #ifdef DLL_PANGO_MODULES
480 fixup_pango_config();
485 void load_custom_fonts()
487 std::string ardour_mono_file;
489 if (!find_file (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
490 cerr << _("Cannot find ArdourMono TrueType font") << endl;
493 FcConfig *config = FcInitLoadConfigAndFonts();
494 FcBool ret = FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(ardour_mono_file.c_str()));
496 if (ret == FcFalse) {
497 cerr << _("Cannot load ArdourMono TrueType font.") << endl;
500 ret = FcConfigSetCurrent(config);
502 if (ret == FcFalse) {
503 cerr << _("Failed to set fontconfig configuration.") << endl;