From 38a180ddc6fe70d467a45938abd76d7bbffafcf0 Mon Sep 17 00:00:00 2001 From: John Emmas Date: Fri, 21 Mar 2014 14:56:59 +0000 Subject: [PATCH] Implement 'fixup_bundle_environment()' for the MSVC build --- gtk2_ardour/bundle_env_msvc.cc | 470 +++++++++++++++++++++++++++++++++ 1 file changed, 470 insertions(+) create mode 100644 gtk2_ardour/bundle_env_msvc.cc diff --git a/gtk2_ardour/bundle_env_msvc.cc b/gtk2_ardour/bundle_env_msvc.cc new file mode 100644 index 0000000000..8a7fcc0d7d --- /dev/null +++ b/gtk2_ardour/bundle_env_msvc.cc @@ -0,0 +1,470 @@ +/* + Copyright (C) 2014 John Emmas + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "bundle_env.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "ardour/ardour.h" +#include "ardour/search_paths.h" +#include "ardour/filesystem_paths.h" + +#include "pbd/file_utils.h" +#include "pbd/epa.h" + +using namespace std; +using namespace PBD; +using namespace ARDOUR; + +std::string +get_windows_drive_volume_letter() +{ +static std::string ret; +char path[PATH_MAX+1]; +LPITEMIDLIST pidl = 0; + + if (!ret.length()) { + if (S_OK == SHGetSpecialFolderLocation (0, CSIDL_WINDOWS, &pidl)) + { + if (SHGetPathFromIDListA (pidl, path)) { + path[2] = '\0'; // Gives us just the drive letter and colon + ret = path; + } + + CoTaskMemFree (pidl); + } + // The above should never fail - but just in case... + else if (char *env_path = getenv ("windir")) + { + strcpy (path, env_path); + path[2] = '\0'; // Gives us just the drive letter and colon + ret = path; + } + } + + return ret; +} + +const string +get_module_folder () +{ +std::string ret; + + // Gives the top-level Ardour installation folder (on Windows) + // Typically, this will be somehwere like "C:\Program Files" + + gchar* pExeRoot = g_win32_get_package_installation_directory_of_module (0); + + if (0 == pExeRoot) { + pExeRoot = g_build_filename("C:\\", "Program Files", PROGRAM_NAME, 0); + } + + if (pExeRoot) { + gchar tmp[PATH_MAX+1]; + gchar* p; + + strcpy(tmp, pExeRoot); + if (0 != (p = strrchr (tmp, G_DIR_SEPARATOR))) { + *p = '\0'; + + if (0 != (p = g_build_filename(tmp, 0))) { + ret = p; + g_free (p); + } + } + + g_free (pExeRoot); + } + + return (ret); +} + +bool +fixup_config_file (Glib::ustring str_file_to_fix) +{ +FILE* fd; +char buf[4096]; +bool conversion_needed = false; +bool succeeded = false; + + fstream file_to_fix (fd = g_fopen(str_file_to_fix.c_str(), "r+b")); + + if (file_to_fix.is_open()) { + vector lines; + std::string line; + + file_to_fix.seekg (0, std::ios::beg); + file_to_fix.seekp (0, std::ios::beg); + + try { + while (!file_to_fix.eof() && file_to_fix.getline (buf, sizeof(buf))) { + line = buf; + + if (!conversion_needed && (std::string::npos != line.find("$("))) + conversion_needed = true; + lines.push_back(line); + } + + if (conversion_needed) { + bool error = false; + std::string::size_type token_begin, token_end; + vector::iterator i; + + for (i = lines.begin(); i != lines.end(); ++i) { + if (string::npos != (token_begin = i->find("$("))) { + if (string::npos != (token_end = i->find(")", token_begin))) { + std::string str_replace_with; + std::string str_to_replace = i->substr(token_begin, ((token_end+1)-token_begin)); + + if (0 == str_to_replace.compare("$(CWD)")) { + // Replace our token with the current working directory + if (getcwd(buf, sizeof(buf))) { + if (buf[strlen(buf)-1] == G_DIR_SEPARATOR) + buf[strlen(buf)-1] = '\0'; + str_replace_with = buf; + + // Replace the first occurrence of our token with the required string + i->erase(token_begin, ((token_end+1)-token_begin)); + i->insert(token_begin, str_replace_with); + } else { + error = true; + } + } else if (0 == str_to_replace.compare("$(WINDRIVE)")){ + // Replace our token with the drive letter (and colon) for the user's Windows volume + str_replace_with = get_windows_drive_volume_letter(); + + // Replace the first occurrence of our token with the required string + i->erase(token_begin, ((token_end+1)-token_begin)); + i->insert(token_begin, str_replace_with); + } else { + // Assume that our token represents an environment variable + std::string envvar_name = str_to_replace.substr(2, str_to_replace.length()-3); + + if (const char *envvar_value = getenv(envvar_name.c_str())) { + strcpy(buf, envvar_value); + if (buf[strlen(buf)-1] == G_DIR_SEPARATOR) + buf[strlen(buf)-1] = '\0'; + str_replace_with = buf; + + // Replace the first occurrence of our token with the required string + i->erase(token_begin, ((token_end+1)-token_begin)); + i->insert(token_begin, str_replace_with); + } else { + error = true; + cerr << "ERROR: unknown environment variable" << endl; + } + } + } + } + } + + if (!error) { + file_to_fix.clear (); // Clear the EOF flag etc + file_to_fix.seekg (0, std::ios::beg); // Seek our 'get' ptr to the file start pos + // (our 'put' ptr shouldn't have moved yet). + chsize(fileno (fd), 0); // Truncate the file, ready for re-writing + + for (i = lines.begin(); i != lines.end(); ++i) { + + // Write the converted contents to our file + file_to_fix << (*i).c_str() << endl; + } + + try { + file_to_fix.close(); + succeeded = true; + } catch (...) {} + } + } else { + file_to_fix.close(); + succeeded = true; + } + } catch (...) { + file_to_fix.close(); + succeeded = false; + } + } else { + cerr << "ERROR: Could not open config file '" << str_file_to_fix << "'" << endl; + } + + return succeeded; +} + +void +fixup_fonts_config () +{ +string fonts_conf_file; + +#ifdef DEBUG + fonts_conf_file = get_module_folder(); + + if (!fonts_conf_file.empty()) { + fonts_conf_file += "\\"; + fonts_conf_file += PROGRAM_NAME; + fonts_conf_file += FONTS_CONF_LOCATION; +#else + if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "fonts.conf", fonts_conf_file)) { +#endif + Glib::setenv ("FONTCONFIG_FILE", fonts_conf_file, true); + + if (0 == fixup_config_file (fonts_conf_file)) + cerr << "ERROR: processing error for 'fonts.conf' file" << endl; + } else { + cerr << "ERROR: Malformed module folder (fonts.conf)" << endl; + } +} + +void +fixup_pango_config () +{ +string pango_modules_file; + +#if defined(DEBUG) || defined(RDC_BUILD) + // Make sure we pick up the debuggable DLLs !!! + pango_modules_file = get_module_folder(); + + if (!pango_modules_file.empty()) { + pango_modules_file += "\\"; + pango_modules_file += PROGRAM_NAME; + pango_modules_file += PANGO_CONF_LOCATION; +/* JE - handy for non-English locale testing (Greek, in this case) +pango_modules_file = Glib::locale_to_utf8("C:\\Program Files\\Mixbus3\\etc\\ΔΗΜΗΤΡΗΣ\\pango.modules"); +/**/ + Glib::ustring pango_modules_path = pango_modules_file; + pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end +#else + if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "pango.modules", pango_modules_file)) { + + Glib::ustring pango_modules_path = pango_modules_file; + pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end +#endif + // Set an environment variable so we can find our pango modules. Note + // that this requires a modified version of libpango (pango-utils.c) + Glib::setenv ("PANGO_MODULE_PATH", Glib::filename_from_utf8(pango_modules_path), true); + + if (0 == fixup_config_file (pango_modules_file)) + cerr << "ERROR: processing error for 'pango.modules' file" << endl; + } else { + cerr << "ERROR: Malformed module folder (pango.modules)" << endl; + } +} + +void +fixup_pixbuf_loaders_config () +{ +string gdk_pixbuf_loaders_file; + +#if defined(DEBUG) || defined(RDC_BUILD) + // Make sure we pick up the debuggable DLLs !!! + gdk_pixbuf_loaders_file = get_module_folder(); + + if (!gdk_pixbuf_loaders_file.empty()) { + gdk_pixbuf_loaders_file += "\\"; + gdk_pixbuf_loaders_file += PROGRAM_NAME; + gdk_pixbuf_loaders_file += PIXBUFLOADERS_CONF_LOCATION; +#else + if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "gdk-pixbuf.loaders", gdk_pixbuf_loaders_file)) { +#endif + // Set an environment variable so we can find our pixbuf modules. + Glib::setenv ("GDK_PIXBUF_MODULE_FILE", Glib::filename_from_utf8(gdk_pixbuf_loaders_file), true); + + if (0 == fixup_config_file (gdk_pixbuf_loaders_file)) + cerr << "ERROR: processing error for 'gdk-pixbuf.loaders' file" << endl; + } else { + cerr << "ERROR: Malformed module folder (gdk-pixbuf.loaders)" << endl; + } +} + +void +fixup_clearlooks_config () +{ +string clearlooks_la_file; + +#if defined(DEBUG) || defined(RDC_BUILD) + // Make sure we pick up the debuggable DLLs !!! + clearlooks_la_file = get_module_folder(); + + if (!clearlooks_la_file.empty()) { + clearlooks_la_file += "\\"; + clearlooks_la_file += PROGRAM_NAME; + clearlooks_la_file += CLEARLOOKS_CONF_LOCATION; +#else + if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "libclearlooks.la", clearlooks_la_file)) { +#endif + // Set an environment variable so we can find our clearlooks engine. + // Note that this requires a modified version of libgtk (gtkthemes.c) + Glib::setenv ("GTK_THEME_ENGINE_FILE", Glib::filename_from_utf8(clearlooks_la_file).c_str(), true); + + if (0 == fixup_config_file (clearlooks_la_file)) + cerr << "ERROR: processing error for 'clearlooks.la' file" << endl; + } else { + cerr << "ERROR: Malformed module folder (clearlooks.la)" << endl; + } +} + +void +fixup_bundle_environment (int argc, char* argv[], const char** localedir) +{ + std::string exec_path = argv[0]; + std::string dir_path = Glib::path_get_dirname (exec_path); + + // Make sure that our runtime CWD is set to Mixbus's install + // folder, regardless of where the caller's CWD was set to. + g_chdir (dir_path.c_str()); + + EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true)); + + // Now set 'dir_path' so we can append some relative paths + dir_path = Glib::path_get_dirname (dir_path); + + std::string path; + const char *cstr; + + // First, set up 'ARDOUR_DLL_PATH' + path = dir_path; + path += "\\lib\\ardour3\\surfaces;"; + path += dir_path; + path += "\\lib\\ardour3\\panners;"; + path += dir_path; + path += "\\lib\\ardour3\\backends;"; + path += dir_path; + path += "\\bin"; + Glib::setenv ("ARDOUR_DLL_PATH", path, true); + + + // Next, set up 'ARDOUR_DATA_PATH' + path = get_module_folder() + "\\"; + path += PROGRAM_NAME; + path += "\\share"; + Glib::setenv ("ARDOUR_DATA_PATH", path, true); + + + // Next, set up 'ARDOUR_CONFIG_PATH' +#ifdef _WIN64 + path = user_config_directory() + "\\win64;"; +#else + path = user_config_directory() + "\\win32;"; +#endif + Glib::setenv ("ARDOUR_CONFIG_PATH", path, true); + + + // Next, set up 'ARDOUR_PATH' + path = user_config_directory(); + path = Glib::path_get_dirname (path); + path += G_SEARCHPATH_SEPARATOR; + path += windows_search_path().to_string(); + path += "\\icons;"; + path += windows_search_path().to_string(); + path += "\\pixmaps;"; + path += ardour_data_search_path().to_string(); // In fact, adds both the 'data' search + path += G_SEARCHPATH_SEPARATOR; // path and our 'config' search path + path += dir_path; + path += "\\etc"; + Glib::setenv ("ARDOUR_PATH", path, true); + + + // Next, set up 'ARDOUR_INSTANT_XML_PATH' + path = user_config_directory(); + Glib::setenv ("ARDOUR_INSTANT_XML_PATH", path, true); + + + // Next, set up 'LADSPA_PATH' + path = ladspa_search_path().to_string(); + Glib::setenv ("LADSPA_PATH", path, true); + + + // Next, set up 'VAMP_PATH' + cstr = getenv ("VAMP_PATH"); + if (cstr) { + path = cstr; + path += G_SEARCHPATH_SEPARATOR; + } else { + path = ""; + } + path += get_module_folder() + "\\"; + path += PROGRAM_NAME; + path += "\\bin\\vamp"; + path += G_SEARCHPATH_SEPARATOR; + path += "%ProgramFiles%\\Vamp Plugins"; + Glib::setenv ("VAMP_PATH", path, true); + + + // Next, set up 'ARDOUR_CONTROL_SURFACE_PATH' + cstr = getenv ("ARDOUR_CONTROL_SURFACE_PATH"); + if (cstr) { + path = cstr; + path += G_SEARCHPATH_SEPARATOR; + } else { + path = ""; + } + path += control_protocol_search_path().to_string(); + Glib::setenv ("ARDOUR_CONTROL_SURFACE_PATH", path, true); + + + // Next, set up 'GTK_LOCALEDIR' + if (ARDOUR::translations_are_enabled ()) { + path = windows_search_path().to_string(); + path += "\\locale"; + Glib::setenv ("GTK_LOCALEDIR", path, true); + } + + + // Next, set up 'GTK_PATH' + cstr = getenv ("GTK_PATH"); + if (cstr) { + path = cstr; + path += G_SEARCHPATH_SEPARATOR; + } else { + path = ""; + } + path += user_config_directory(); + path += "\\.gtk-2.0"; + Glib::setenv ("GTK_PATH", path, true); + + + // Unset GTK_RC_FILES so that we only load the RC files that we define + Glib::unsetenv ("GTK_RC_FILES"); + + + // and set a '$HOME' environment variable. This variable changes the value returned + // by 'g_get_home_dir()' so to prevent that function from unexpectedly changing its + // mind, we'll set '$HOME' to whatever 'g_get_home_dir()' is already returning!! + if (NULL == getenv("HOME")) { + Glib::setenv ("HOME", Glib::locale_from_utf8(g_get_home_dir()), true); + } + + fixup_fonts_config(); + fixup_pango_config(); + fixup_clearlooks_config(); + fixup_pixbuf_loaders_config(); +} + + +void load_custom_fonts() +{ +}