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