2 Copyright (C) 2014 John Emmas
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.
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.
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.
20 #include "bundle_env.h"
31 #include <glib/gstdio.h>
33 #include <fontconfig/fontconfig.h>
35 #include "ardour/ardour.h"
36 #include "ardour/search_paths.h"
37 #include "ardour/filesystem_paths.h"
39 #include "pbd/file_utils.h"
44 using namespace ARDOUR;
47 get_windows_drive_volume_letter()
49 static std::string ret;
50 char path[PATH_MAX+1];
51 LPITEMIDLIST pidl = 0;
54 if (S_OK == SHGetSpecialFolderLocation (0, CSIDL_WINDOWS, &pidl))
56 if (SHGetPathFromIDListA (pidl, path)) {
57 path[2] = '\0'; // Gives us just the drive letter and colon
63 // The above should never fail - but just in case...
64 else if (char *env_path = getenv ("windir"))
66 strcpy (path, env_path);
67 path[2] = '\0'; // Gives us just the drive letter and colon
80 // Gives the top-level Ardour installation folder (on Windows)
81 // Typically, this will be somehwere like "C:\Program Files"
83 gchar* pExeRoot = g_win32_get_package_installation_directory_of_module (0);
86 pExeRoot = g_build_filename("C:\\", "Program Files", PROGRAM_NAME, 0);
90 gchar tmp[PATH_MAX+1];
93 strcpy(tmp, pExeRoot);
94 if (0 != (p = strrchr (tmp, G_DIR_SEPARATOR))) {
97 if (0 != (p = g_build_filename(tmp, 0))) {
110 fixup_config_file (Glib::ustring str_file_to_fix)
114 bool conversion_needed = false;
115 bool succeeded = false;
117 fstream file_to_fix (fd = g_fopen(str_file_to_fix.c_str(), "r+b"));
119 if (file_to_fix.is_open()) {
120 vector<std::string> lines;
123 file_to_fix.seekg (0, std::ios::beg);
124 file_to_fix.seekp (0, std::ios::beg);
127 while (!file_to_fix.eof() && file_to_fix.getline (buf, sizeof(buf))) {
130 if (!conversion_needed && (std::string::npos != line.find("$(")))
131 conversion_needed = true;
132 lines.push_back(line);
135 if (conversion_needed) {
137 std::string::size_type token_begin, token_end;
138 vector<string>::iterator i;
140 for (i = lines.begin(); i != lines.end(); ++i) {
141 if (string::npos != (token_begin = i->find("$("))) {
142 if (string::npos != (token_end = i->find(")", token_begin))) {
143 std::string str_replace_with;
144 std::string str_to_replace = i->substr(token_begin, ((token_end+1)-token_begin));
146 if (0 == str_to_replace.compare("$(CWD)")) {
147 // Replace our token with the current working directory
148 if (getcwd(buf, sizeof(buf))) {
149 if (buf[strlen(buf)-1] == G_DIR_SEPARATOR)
150 buf[strlen(buf)-1] = '\0';
151 str_replace_with = buf;
153 // Replace the first occurrence of our token with the required string
154 i->erase(token_begin, ((token_end+1)-token_begin));
155 i->insert(token_begin, str_replace_with);
159 } else if (0 == str_to_replace.compare("$(WINDRIVE)")){
160 // Replace our token with the drive letter (and colon) for the user's Windows volume
161 str_replace_with = get_windows_drive_volume_letter();
163 // Replace the first occurrence of our token with the required string
164 i->erase(token_begin, ((token_end+1)-token_begin));
165 i->insert(token_begin, str_replace_with);
167 // Assume that our token represents an environment variable
168 std::string envvar_name = str_to_replace.substr(2, str_to_replace.length()-3);
170 if (const char *envvar_value = getenv(envvar_name.c_str())) {
171 strcpy(buf, envvar_value);
172 if (buf[strlen(buf)-1] == G_DIR_SEPARATOR)
173 buf[strlen(buf)-1] = '\0';
174 str_replace_with = buf;
176 // Replace the first occurrence of our token with the required string
177 i->erase(token_begin, ((token_end+1)-token_begin));
178 i->insert(token_begin, str_replace_with);
181 cerr << "ERROR: unknown environment variable" << endl;
189 file_to_fix.clear (); // Clear the EOF flag etc
190 file_to_fix.seekg (0, std::ios::beg); // Seek our 'get' ptr to the file start pos
191 // (our 'put' ptr shouldn't have moved yet).
192 chsize(fileno (fd), 0); // Truncate the file, ready for re-writing
194 for (i = lines.begin(); i != lines.end(); ++i) {
196 // Write the converted contents to our file
197 file_to_fix << (*i).c_str() << endl;
214 cerr << "ERROR: Could not open config file '" << str_file_to_fix << "'" << endl;
221 fixup_fonts_config ()
223 string fonts_conf_file;
226 fonts_conf_file = get_module_folder();
228 if (!fonts_conf_file.empty()) {
229 fonts_conf_file += "\\";
230 fonts_conf_file += PROGRAM_NAME;
231 fonts_conf_file += FONTS_CONF_LOCATION;
233 if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "fonts.conf", fonts_conf_file)) {
235 Glib::setenv ("FONTCONFIG_FILE", fonts_conf_file, true);
237 if (0 == fixup_config_file (fonts_conf_file))
238 cerr << "ERROR: processing error for 'fonts.conf' file" << endl;
240 cerr << "ERROR: Malformed module folder (fonts.conf)" << endl;
245 fixup_pango_config ()
247 string pango_modules_file;
249 #if defined(DEBUG) || defined(RDC_BUILD)
250 // Make sure we pick up the debuggable DLLs !!!
251 pango_modules_file = get_module_folder();
253 if (!pango_modules_file.empty()) {
254 pango_modules_file += "\\";
255 pango_modules_file += PROGRAM_NAME;
256 pango_modules_file += PANGO_CONF_LOCATION;
258 // JE - handy for non-English locale testing (Greek, in this case)
259 Glib::ustring pango_modules_path = Glib::locale_to_utf8("C:\\Program Files\\Mixbus3\\etc\\��������\\pango.modules");
262 Glib::ustring pango_modules_path = pango_modules_file;
264 pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end
266 if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "pango.modules", pango_modules_file)) {
268 Glib::ustring pango_modules_path = pango_modules_file;
269 pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end
271 // Set an environment variable so we can find our pango modules. Note
272 // that this requires a modified version of libpango (pango-utils.c)
273 Glib::setenv ("PANGO_MODULE_PATH", Glib::filename_from_utf8(pango_modules_path), true);
275 if (0 == fixup_config_file (pango_modules_file))
276 cerr << "ERROR: processing error for 'pango.modules' file" << endl;
278 cerr << "ERROR: Malformed module folder (pango.modules)" << endl;
283 fixup_pixbuf_loaders_config ()
285 string gdk_pixbuf_loaders_file;
287 #if defined(DEBUG) || defined(RDC_BUILD)
288 // Make sure we pick up the debuggable DLLs !!!
289 gdk_pixbuf_loaders_file = get_module_folder();
291 if (!gdk_pixbuf_loaders_file.empty()) {
292 gdk_pixbuf_loaders_file += "\\";
293 gdk_pixbuf_loaders_file += PROGRAM_NAME;
294 gdk_pixbuf_loaders_file += PIXBUFLOADERS_CONF_LOCATION;
296 if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "gdk-pixbuf.loaders", gdk_pixbuf_loaders_file)) {
298 // Set an environment variable so we can find our pixbuf modules.
299 Glib::setenv ("GDK_PIXBUF_MODULE_FILE", Glib::filename_from_utf8(gdk_pixbuf_loaders_file), true);
301 if (0 == fixup_config_file (gdk_pixbuf_loaders_file))
302 cerr << "ERROR: processing error for 'gdk-pixbuf.loaders' file" << endl;
304 cerr << "ERROR: Malformed module folder (gdk-pixbuf.loaders)" << endl;
309 fixup_clearlooks_config ()
311 string clearlooks_la_file;
313 #if defined(DEBUG) || defined(RDC_BUILD)
314 // Make sure we pick up the debuggable DLLs !!!
315 clearlooks_la_file = get_module_folder();
317 if (!clearlooks_la_file.empty()) {
318 clearlooks_la_file += "\\";
319 clearlooks_la_file += PROGRAM_NAME;
320 clearlooks_la_file += CLEARLOOKS_CONF_LOCATION;
322 if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "libclearlooks.la", clearlooks_la_file)) {
324 // Set an environment variable so we can find our clearlooks engine.
325 // Note that this requires a modified version of libgtk (gtkthemes.c)
326 Glib::setenv ("GTK_THEME_ENGINE_FILE", Glib::filename_from_utf8(clearlooks_la_file).c_str(), true);
328 if (0 == fixup_config_file (clearlooks_la_file))
329 cerr << "ERROR: processing error for 'clearlooks.la' file" << endl;
331 cerr << "ERROR: Malformed module folder (clearlooks.la)" << endl;
336 fixup_bundle_environment (int argc, char* argv[], const char** localedir)
338 std::string exec_path = argv[0];
339 std::string dir_path = Glib::path_get_dirname (exec_path);
341 // Make sure that our runtime CWD is set to Mixbus's install
342 // folder, regardless of where the caller's CWD was set to.
343 g_chdir (dir_path.c_str());
345 EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true));
347 // Now set 'dir_path' so we can append some relative paths
348 dir_path = Glib::path_get_dirname (dir_path);
353 // First, set up 'ARDOUR_DLL_PATH'
355 path += "\\lib\\ardour3\\surfaces;";
357 path += "\\lib\\ardour3\\panners;";
359 path += "\\lib\\ardour3\\backends;";
362 Glib::setenv ("ARDOUR_DLL_PATH", path, true);
365 // Next, set up 'ARDOUR_DATA_PATH'
366 path = get_module_folder() + "\\";
367 path += PROGRAM_NAME;
369 Glib::setenv ("ARDOUR_DATA_PATH", path, true);
372 // Next, set up 'ARDOUR_CONFIG_PATH'
374 path = user_config_directory() + "\\win64;";
376 path = user_config_directory() + "\\win32;";
378 Glib::setenv ("ARDOUR_CONFIG_PATH", path, true);
381 // Next, set up 'ARDOUR_PATH'
382 path = user_config_directory();
383 path = Glib::path_get_dirname (path);
384 path += G_SEARCHPATH_SEPARATOR;
385 path += windows_search_path().to_string();
387 path += windows_search_path().to_string();
388 path += "\\pixmaps;";
389 path += ardour_data_search_path().to_string(); // In fact, adds both the 'data' search
390 path += G_SEARCHPATH_SEPARATOR; // path and our 'config' search path
393 Glib::setenv ("ARDOUR_PATH", path, true);
396 // Next, set up 'ARDOUR_INSTANT_XML_PATH'
397 path = user_config_directory();
398 Glib::setenv ("ARDOUR_INSTANT_XML_PATH", path, true);
401 // Next, set up 'LADSPA_PATH'
402 path = ladspa_search_path().to_string();
403 Glib::setenv ("LADSPA_PATH", path, true);
406 // Next, set up 'VAMP_PATH'
407 cstr = getenv ("VAMP_PATH");
410 path += G_SEARCHPATH_SEPARATOR;
414 path += get_module_folder() + "\\";
415 path += PROGRAM_NAME;
416 path += "\\bin\\vamp";
417 path += G_SEARCHPATH_SEPARATOR;
418 path += "%ProgramFiles%\\Vamp Plugins";
419 Glib::setenv ("VAMP_PATH", path, true);
422 // Next, set up 'ARDOUR_CONTROL_SURFACE_PATH'
423 cstr = getenv ("ARDOUR_CONTROL_SURFACE_PATH");
426 path += G_SEARCHPATH_SEPARATOR;
430 path += control_protocol_search_path().to_string();
431 Glib::setenv ("ARDOUR_CONTROL_SURFACE_PATH", path, true);
434 // Next, set up 'GTK_LOCALEDIR'
435 if (ARDOUR::translations_are_enabled ()) {
436 path = windows_search_path().to_string();
438 Glib::setenv ("GTK_LOCALEDIR", path, true);
442 // Next, set up 'GTK_PATH'
443 cstr = getenv ("GTK_PATH");
446 path += G_SEARCHPATH_SEPARATOR;
450 path += user_config_directory();
451 path += "\\.gtk-2.0";
452 Glib::setenv ("GTK_PATH", path, true);
455 // Unset GTK_RC_FILES so that we only load the RC files that we define
456 Glib::unsetenv ("GTK_RC_FILES");
459 // and set a '$HOME' environment variable. This variable changes the value returned
460 // by 'g_get_home_dir()' so to prevent that function from unexpectedly changing its
461 // mind, we'll set '$HOME' to whatever 'g_get_home_dir()' is already returning!!
462 if (NULL == getenv("HOME")) {
463 Glib::setenv ("HOME", Glib::locale_from_utf8(g_get_home_dir()), true);
466 fixup_fonts_config();
467 fixup_pango_config();
468 fixup_clearlooks_config();
469 fixup_pixbuf_loaders_config();
473 void load_custom_fonts()
475 std::string ardour_mono_file;
477 if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
478 cerr << "Cannot find ArdourMono TrueType font" << endl;
481 FcConfig *config = FcInitLoadConfigAndFonts();
482 FcBool ret = FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(ardour_mono_file.c_str()));
484 if (ret == FcFalse) {
485 cerr << "Cannot load ArdourMono TrueType font." << endl;
488 ret = FcConfigSetCurrent(config);
490 if (ret == FcFalse) {
491 cerr << "Failed to set fontconfig configuration." << endl;