ardour
bundle_env_msvc.cc
Go to the documentation of this file.
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 "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 <glib/gstdio.h>
33 
34 #include <fontconfig/fontconfig.h>
35 
36 #include "ardour/ardour.h"
37 #include "ardour/search_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
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
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
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 += FONTS_CONF_LOCATION;
240 #else
241  if (PBD::find_file (ARDOUR::ardour_config_search_path(), "fonts.conf", fonts_conf_file)) {
242 #endif
243  Glib::setenv ("FONTCONFIG_FILE", fonts_conf_file, true);
244 
245  if (0 == fixup_config_file (fonts_conf_file))
246  cerr << _("ERROR: processing error for 'fonts.conf' file") << endl;
247  } else {
248  cerr << _("ERROR: Malformed module folder (fonts.conf)") << endl;
249  }
250 }
251 
252 void
254 {
255 string pango_modules_file;
256 
257 #if defined(DEBUG) || defined(RDC_BUILD)
258  // Make sure we pick up the debuggable DLLs !!!
259  pango_modules_file = get_module_folder();
260 
261  if (!pango_modules_file.empty()) {
262  pango_modules_file += "\\";
263  pango_modules_file += PROGRAM_NAME;
264  pango_modules_file += PANGO_CONF_LOCATION;
265 #if 0
266 // JE - handy for non-English locale testing (Greek, in this case)
267  Glib::ustring pango_modules_path = Glib::locale_to_utf8("C:\\Program Files\\Mixbus3\\etc\\ΔΗΜΗΤΡΗΣ\\pango.modules");
268 
269 #else
270  Glib::ustring pango_modules_path = pango_modules_file;
271 #endif
272  pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end
273 #else
274  if (PBD::find_file (ARDOUR::ardour_config_search_path(), "pango.modules", pango_modules_file)) {
275 
276  Glib::ustring pango_modules_path = pango_modules_file;
277  pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end
278 #endif
279  // Set an environment variable so we can find our pango modules. Note
280  // that this requires a modified version of libpango (pango-utils.c)
281  Glib::setenv ("PANGO_MODULE_PATH", Glib::filename_from_utf8(pango_modules_path), true);
282 
283  if (0 == fixup_config_file (pango_modules_file))
284  cerr << _("ERROR: processing error for 'pango.modules' file") << endl;
285  } else {
286  cerr << _("ERROR: Malformed module folder (pango.modules)") << endl;
287  }
288 }
289 
290 void
292 {
293 string gdk_pixbuf_loaders_file;
294 
295 #if defined(DEBUG) || defined(RDC_BUILD)
296  // Make sure we pick up the debuggable DLLs !!!
297  gdk_pixbuf_loaders_file = get_module_folder();
298 
299  if (!gdk_pixbuf_loaders_file.empty()) {
300  gdk_pixbuf_loaders_file += "\\";
301  gdk_pixbuf_loaders_file += PROGRAM_NAME;
302  gdk_pixbuf_loaders_file += PIXBUFLOADERS_CONF_LOCATION;
303 #else
304  if (PBD::find_file (ARDOUR::ardour_config_search_path(), "gdk-pixbuf.loaders", gdk_pixbuf_loaders_file)) {
305 #endif
306  // Set an environment variable so we can find our pixbuf modules.
307  Glib::setenv ("GDK_PIXBUF_MODULE_FILE", Glib::filename_from_utf8(gdk_pixbuf_loaders_file), true);
308 
309  if (0 == fixup_config_file (gdk_pixbuf_loaders_file))
310  cerr << _("ERROR: processing error for 'gdk-pixbuf.loaders' file") << endl;
311  } else {
312  cerr << _("ERROR: Malformed module folder (gdk-pixbuf.loaders)") << endl;
313  }
314 }
315 
316 void
318 {
319 string clearlooks_la_file;
320 
321 #if defined(DEBUG) || defined(RDC_BUILD)
322  // Make sure we pick up the debuggable DLLs !!!
323  clearlooks_la_file = get_module_folder();
324 
325  if (!clearlooks_la_file.empty()) {
326  clearlooks_la_file += "\\";
327  clearlooks_la_file += PROGRAM_NAME;
328  clearlooks_la_file += CLEARLOOKS_CONF_LOCATION;
329 #else
330  if (PBD::find_file (ARDOUR::ardour_config_search_path(), "libclearlooks.la", clearlooks_la_file)) {
331 #endif
332  // Set an environment variable so we can find our clearlooks engine.
333  // Note that this requires a modified version of libgtk (gtkthemes.c)
334  Glib::setenv ("GTK_THEME_ENGINE_FILE", Glib::filename_from_utf8(clearlooks_la_file).c_str(), true);
335 
336  if (0 == fixup_config_file (clearlooks_la_file))
337  cerr << _("ERROR: processing error for 'clearlooks.la' file") << endl;
338  } else {
339  cerr << _("ERROR: Malformed module folder (clearlooks.la)") << endl;
340  }
341 }
342 
343 void
344 fixup_bundle_environment (int argc, char* argv[], string & localedir)
345 {
346  std::string exec_path = argv[0];
347  std::string dir_path = Glib::path_get_dirname (exec_path);
348 
349  // Make sure that our runtime CWD is set to Mixbus's install
350  // folder, regardless of where the caller's CWD was set to.
351  g_chdir (dir_path.c_str());
352 
353  EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true));
354 
355  // Now set 'dir_path' so we can append some relative paths
356  dir_path = Glib::path_get_dirname (dir_path);
357 
358  std::string path;
359  const char *cstr;
360 
361  // First, set up 'ARDOUR_DLL_PATH'
362  path = dir_path;
363  path += "\\lib\\ardour3\\surfaces;";
364  path += dir_path;
365  path += "\\lib\\ardour3\\panners;";
366  path += dir_path;
367  path += "\\lib\\ardour3\\backends;";
368  path += dir_path;
369  path += "\\bin";
370  Glib::setenv ("ARDOUR_DLL_PATH", path, true);
371 
372 
373  // Next, set up 'ARDOUR_DATA_PATH'
374  path = get_module_folder() + "\\";
375  path += PROGRAM_NAME;
376  path += "\\share";
377  Glib::setenv ("ARDOUR_DATA_PATH", path, true);
378 
379 
380  // Next, set up 'ARDOUR_CONFIG_PATH'
381 #ifdef _WIN64
382  path = user_config_directory() + "\\win64;";
383 #else
384  path = user_config_directory() + "\\win32;";
385 #endif
386  Glib::setenv ("ARDOUR_CONFIG_PATH", path, true);
387 
388 
389  // Next, set up 'ARDOUR_PATH'
390  path = user_config_directory();
391  path = Glib::path_get_dirname (path);
392  path += G_SEARCHPATH_SEPARATOR;
393  path += windows_search_path().to_string();
394  path += "\\icons;";
395  path += windows_search_path().to_string();
396  path += "\\pixmaps;";
397  path += ardour_data_search_path().to_string(); // In fact, adds both the 'data' search
398  path += G_SEARCHPATH_SEPARATOR; // path and our 'config' search path
399  path += dir_path;
400  path += "\\etc";
401  Glib::setenv ("ARDOUR_PATH", path, true);
402 
403 
404  // Next, set up 'ARDOUR_INSTANT_XML_PATH'
405  path = user_config_directory();
406  Glib::setenv ("ARDOUR_INSTANT_XML_PATH", path, true);
407 
408 
409  // Next, set up 'LADSPA_PATH'
410  path = ladspa_search_path().to_string();
411  Glib::setenv ("LADSPA_PATH", path, true);
412 
413 
414  // Next, set up 'SUIL_MODULE_DIR'
415  Glib::setenv ("SUIL_MODULE_DIR", Glib::build_filename(ardour_dll_directory(), "suil"), true);
416 
417 
418  // Next, set up 'VAMP_PATH'
419  cstr = getenv ("VAMP_PATH");
420  if (cstr) {
421  path = cstr;
422  path += G_SEARCHPATH_SEPARATOR;
423  } else {
424  path = "";
425  }
426  path += get_module_folder() + "\\";
427  path += PROGRAM_NAME;
428  path += "\\bin\\vamp";
429  path += G_SEARCHPATH_SEPARATOR;
430  path += "%ProgramFiles%\\Vamp Plugins";
431  Glib::setenv ("VAMP_PATH", path, true);
432 
433 
434  // Next, set up 'ARDOUR_CONTROL_SURFACE_PATH'
435  cstr = getenv ("ARDOUR_CONTROL_SURFACE_PATH");
436  if (cstr) {
437  path = cstr;
438  path += G_SEARCHPATH_SEPARATOR;
439  } else {
440  path = "";
441  }
443  Glib::setenv ("ARDOUR_CONTROL_SURFACE_PATH", path, true);
444 
445 
446  // Next, set up 'GTK_LOCALEDIR'
448  path = windows_search_path().to_string();
449  path += "\\locale";
450  Glib::setenv ("GTK_LOCALEDIR", path, true);
451 
452  // and return the same path to our caller
453  localedir = path;
454  }
455 
456 
457  // Next, set up 'GTK_PATH'
458  cstr = getenv ("GTK_PATH");
459  if (cstr) {
460  path = cstr;
461  path += G_SEARCHPATH_SEPARATOR;
462  } else {
463  path = "";
464  }
465  path += user_config_directory();
466  path += "\\.gtk-2.0";
467  Glib::setenv ("GTK_PATH", path, true);
468 
469 
470  // Unset GTK_RC_FILES so that we only load the RC files that we define
471  Glib::unsetenv ("GTK_RC_FILES");
472 
473 
474  // and set a '$HOME' environment variable. This variable changes the value returned
475  // by 'g_get_home_dir()' so to prevent that function from unexpectedly changing its
476  // mind, we'll set '$HOME' to whatever 'g_get_home_dir()' is already returning!!
477  if (NULL == getenv("HOME")) {
478  Glib::setenv ("HOME", Glib::locale_from_utf8(g_get_home_dir()), true);
479  }
480 
483 
484 #ifdef DLL_PIXBUF_LOADERS
486 #endif
487 #ifdef DLL_PANGO_MODULES
489 #endif
490 }
491 
492 
494 {
495  std::string ardour_mono_file;
496 
497  if (!find_file (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
498  cerr << _("Cannot find ArdourMono TrueType font") << endl;
499  }
500 
501  FcConfig *config = FcInitLoadConfigAndFonts();
502  FcBool ret = FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(ardour_mono_file.c_str()));
503 
504  if (ret == FcFalse) {
505  cerr << _("Cannot load ArdourMono TrueType font.") << endl;
506  }
507 
508  ret = FcConfigSetCurrent(config);
509 
510  if (ret == FcFalse) {
511  cerr << _("Failed to set fontconfig configuration.") << endl;
512  }
513 }
std::string get_windows_drive_volume_letter()
LIBARDOUR_API PBD::Searchpath ladspa_search_path()
Definition: search_paths.cc:89
const string get_module_folder()
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
LIBARDOUR_API bool translations_are_enabled()
Definition: globals.cc:636
bool find_file(const Searchpath &search_path, const string &filename, std::string &result)
Definition: file_utils.cc:187
void fixup_clearlooks_config()
#define _(Text)
Definition: i18n.h:11
bool fixup_config_file(Glib::ustring str_file_to_fix)
LIBARDOUR_API std::string user_config_directory(int version=-1)
#define PATH_MAX
Definition: lv2_plugin.h:34
LIBARDOUR_API PBD::Searchpath control_protocol_search_path()
Definition: search_paths.cc:62
static const char * localedir
Definition: load_session.cc:12
Definition: amp.h:29
void load_custom_fonts()
LIBARDOUR_API std::string ardour_dll_directory()
LIBARDOUR_API PBD::Searchpath ardour_config_search_path()
LIBARDOUR_API std::string user_cache_directory()
LIBPBD_TEMPLATE_MEMBER_API const std::string to_string() const
Definition: search_path.cc:99
void fixup_fonts_config()
Definition: debug.h:30
LIBARDOUR_API PBD::Searchpath ardour_data_search_path()
void fixup_bundle_environment(int argc, char *argv[], string &localedir)
void fixup_pixbuf_loaders_config()
void fixup_pango_config()