1 /*
2  * Copyright (C) 2009, 2010 Hermann Meyer, James Warden, Andreas Degert
3  * Copyright (C) 2011 Pete Shorthose
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  * ---------------------------------------------------------------------------
19  *
20  *    This is the gx_head system interface
21  *
22  * ----------------------------------------------------------------------------
23  */
24 
25 #include <dirent.h>
26 #include <iostream>
27 #include <iomanip>                  // NOLINT
28 
29 #include "engine.h"
30 
31 namespace gx_system {
32 
33 /****************************************************************
34  ** Measuring times (only when debugging)
35  */
36 
37 #ifndef NDEBUG
38 
reset()39 void Measure::reset() {
40     period.reset();
41     duration.reset();
42     duration1.reset();
43     duration2.reset();
44     FPUStatus1 = 0;
45     FPUStatus2 = 0;
46     MXStatus1 = 0;
47     MXStatus2 = 0;
48 }
49 
print_accum(const Accum & accum,const char * prefix,bool verbose,int total) const50 void Measure::print_accum(const Accum& accum, const char* prefix, bool verbose, int total) const {
51     streamsize prec = cout.precision();
52     ios_base::fmtflags flags = cout.flags();
53     cout << prefix << "mean: " << fixed << setprecision(4) << ns2ms(accum.mean());
54     if (total > 0) {
55         cout << " (" << setprecision(2) << 100.0*accum.mean()/static_cast<float>(total) << "%)";
56     }
57     cout << ", min: " << setprecision(4) << ns2ms(accum.minimum())
58          << ", max: " << ns2ms(accum.maximum());
59     if (total > 0) {
60         cout << " (" << setprecision(2) << 100.0*accum.maximum()/static_cast<float>(total) << "%)";
61     }
62     if (verbose) {
63         cout << ", stddev: " << setprecision(4) << ns2ms(accum.stddev())
64              << ", n: " << accum.count();
65     }
66     cout << endl;
67     cout.precision(prec);
68     cout.flags(flags);
69 }
70 
print_status(const char * title,unsigned int status)71 static void print_status(const char *title, unsigned int status) {
72     // print list of names for active bits in "status"
73     // bits for mmx and x87 have different symbolic names in
74     // header files but are actually identical so that this function
75     // can be used for both types status word
76     Glib::ustring s;
77     if (status & FE_INVALID) {
78 	if (!s.empty()) {
79 	    s += ",";
80 	}
81 	s += "invalid";
82     }
83 #ifdef FE_DENORM
84     if (status & FE_DENORM) {
85 	if (!s.empty()) {
86 	    s += ",";
87 	}
88 	s += "denormal";
89     }
90 #endif
91     if (status & FE_DIVBYZERO) {
92 	if (!s.empty()) {
93 	    s += ",";
94 	}
95 	s += "zerodivide";
96     }
97     if (status & FE_OVERFLOW) {
98 	if (!s.empty()) {
99 	    s += ",";
100 	}
101 	s += "overflow";
102     }
103     if (status & FE_UNDERFLOW) {
104 	if (!s.empty()) {
105 	    s += ",";
106 	}
107 	s += "underflow";
108     }
109     if (!s.empty()) {
110 	cout << title << s << endl;
111     }
112 }
113 
print(bool verbose) const114 void Measure::print(bool verbose) const {
115     if (verbose) {
116         print_accum(period,    "period     ", verbose);
117 	print_accum(duration1, "duration1  ", verbose, period.mean());
118 	print_accum(duration2, "duration2  ", verbose, period.mean());
119 	print_accum(duration,  "duration   ", verbose, period.mean());
120     } else {
121         print_accum(duration, "duration  ", false, period.mean());
122     }
123     print_status("FPU status: ", FPUStatus1 | FPUStatus2);
124     print_status("MX status: ", MXStatus1 | MXStatus2);
125 }
126 
MeasureThreadsafe()127 MeasureThreadsafe::MeasureThreadsafe()
128     : m(), pmeasure(m), t1s(), t1e(), t2s(), t1old(), FPUStatus(), MXStatus() {
129 }
130 
print(bool verbose)131 void MeasureThreadsafe::print(bool verbose) {
132     Measure *p = pmeasure;
133     Measure *pn;
134     if (p == m) {
135         pn = m+1;
136     } else {
137         pn = m;
138     }
139     atomic_set(&pmeasure, pn);
140     p->print(verbose);
141     p->reset();
142 }
143 
144 MeasureThreadsafe measure;
145 
add_time_measurement()146 void add_time_measurement() {
147     char *p = getenv("GUITARIX_MEASURE");
148     if (!p) {
149         return;
150     }
151     bool verbose = false;
152     if (strcmp(p, "1") == 0) {
153         verbose = true;
154     }
155     Glib::signal_timeout().connect(
156 	sigc::bind_return(
157 	    sigc::bind(
158 		sigc::mem_fun(measure, &MeasureThreadsafe::print),
159 		verbose),
160 	    true),
161 	1000);
162 }
163 
164 #endif
165 
166 
167 /****************************************************************
168  ** class SkinHandling
169  */
170 
set_styledir(const string & style_dir)171 void SkinHandling::set_styledir(const string& style_dir) {
172     // fetch all skin names in directory
173     DIR *d;
174     d = opendir(style_dir.c_str());
175     if (!d) {
176         return;
177     }
178     // look for gx_head_*.css and extract *-part
179     struct dirent *de;
180     skin_list.clear();
181     while ((de = readdir(d)) != 0) {
182         char *p = de->d_name;
183         if (strncmp(p, "gx_head_", 8) != 0) {
184             continue;
185         }
186         if (strncmp(p, "gx_head_gx", 10) == 0) {
187             continue;
188         }
189         p += 8;
190         int n = strlen(p) - 4;
191         if (strcmp(p+n, ".css") != 0) {
192             continue;
193         }
194         skin_list.push_back(string(p, n));
195     }
196     closedir(d);
197     sort(skin_list.begin(), skin_list.end());
198 }
199 
get_cssfile() const200 string SkinHandling::get_cssfile() const {
201     if (name.empty()) {
202 	return "minimal.css";
203     }
204     return "gx_head_" + name + ".css";
205 }
206 
is_in_list(const string & name)207 bool SkinHandling::is_in_list(const string& name) {
208     return index(name) < skin_list.size();
209 }
210 
index(const Glib::ustring & name)211 unsigned int SkinHandling::index(const Glib::ustring& name) {
212     unsigned int i = 0;
213     for (; i < skin_list.size(); ++i) {
214 	if (skin_list[i] == name) {
215 	    break;
216 	}
217     }
218     return i;
219 }
220 
operator [](unsigned int idx)221 const Glib::ustring& SkinHandling::operator[](unsigned int idx) {
222     if (idx < skin_list.size()) {
223 	return skin_list[idx];
224     } else {
225 	static Glib::ustring empty;
226 	return empty;
227     }
228 }
229 
set_default_skin_name()230 void SkinHandling::set_default_skin_name() {
231     name = "Guitarix";
232 }
233 
234 /****************************************************************
235  ** class PathList
236  */
237 
PathList(const char * env_name)238 PathList::PathList(const char *env_name): dirs() {
239     if (!env_name) {
240 	return;
241     }
242     const char *p = getenv(env_name);
243     if (!p) {
244 	return;
245     }
246     while (true) {
247 	const char *q = strchr(p, ':');
248 	if (q) {
249 	    int n = q - p;
250 	    if (n) {
251 		add(std::string(p, n));
252 	    }
253 	    p = q+1;
254 	} else {
255 	    if (*p) {
256 		add(p);
257 	    }
258 	    break;
259 	}
260     }
261 }
262 
contains(const string & d) const263 bool PathList::contains(const string& d) const {
264     Glib::RefPtr<Gio::File> f = Gio::File::create_for_path(d);
265     for (pathlist::const_iterator i = dirs.begin();
266 	 i != dirs.end(); ++i) {
267 	if (f->equal(*i)) {
268 	    return true;
269 	}
270     }
271     return false;
272 }
273 
274 
find_dir(std::string * d,const std::string & filename) const275 bool PathList::find_dir(std::string* d, const std::string& filename) const {
276     for (pathlist::const_iterator i = dirs.begin();
277 	 i != dirs.end(); ++i) {
278 	string p = (*i)->get_path();
279 	string fn = Glib::build_filename(p, filename);
280 	if (access(fn.c_str(), R_OK) == 0) {
281 	    *d = p;
282 	    return true;
283 	    }
284 	}
285     return false;
286 }
287 
288 
289 /****************************************************************
290  ** class PrefixConverter
291  */
292 
add(char s,const std::string & d)293 void PrefixConverter::add(char s, const std::string& d) {
294     assert(s != '%');
295     dirs[s] = (d[d.size()-1] == '/' ? d.substr(0,d.size()-1) : d);
296 }
297 
replace_symbol(const std::string & dir) const298 std::string PrefixConverter::replace_symbol(const std::string& dir) const {
299     if (dir.size() < 2 || dir[0] != '%') {
300 	return dir;
301     }
302     symbol_path_map::const_iterator i = dirs.find(dir[1]);
303     if (i != dirs.end()) {
304 	return Glib::build_filename(i->second, dir.substr(2));
305     }
306     if (dir.compare(0, 2, "%%")) {
307 	return dir.substr(1);
308     }
309     return dir;
310 }
311 
replace_path(const std::string & dir) const312 std::string PrefixConverter::replace_path(const std::string& dir) const {
313     for (symbol_path_map::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
314 	size_t n = i->second.size();
315 	if (dir.compare(0, n, i->second) == 0) {
316 	    std::string tail = dir.substr(n);
317 	    if (Glib::build_filename(i->second, tail) == dir) {
318 		std::string sym = "%";
319 		sym.push_back(i->first);
320 		return sym + tail;
321 	    }
322 	}
323     }
324     if (dir.size() < 2 || dir[0] != '%') {
325 	return dir;
326     }
327     return "%" + dir;
328 }
329 
330 
331 /*****************************************************************
332  ** class DirectoryListing
333  */
334 
IRFileListing(const std::string & path)335 IRFileListing::IRFileListing(const std::string& path) {
336     Glib::RefPtr<Gio::File> file = Gio::File::create_for_path(path);
337     if (file->query_exists()) {
338         Glib::RefPtr<Gio::FileEnumerator> child_enumeration =
339               file->enumerate_children(G_FILE_ATTRIBUTE_STANDARD_NAME
340 				       "," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
341 				       "," G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
342         Glib::RefPtr<Gio::FileInfo> file_info;
343         while ((file_info = child_enumeration->next_file())) {
344             std::string content_type = file_info->get_attribute_string(
345                 G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
346 	    if (content_type.substr(0, 6) == "audio/") {
347 		listing.push_back(
348 		    FileName(
349 			file_info->get_attribute_byte_string(G_FILE_ATTRIBUTE_STANDARD_NAME),
350 			file_info->get_attribute_string(G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME)));
351 	    }
352         }
353     } else {
354         gx_print_error(
355 	    "jconvolver",
356 	    boost::format(_("Error reading file path %1%")) % path);
357     }
358 }
359 
list_subdirs(const Glib::RefPtr<Gio::File> & file,std::vector<FileName> & dirs,const Glib::ustring & prefix)360 static void list_subdirs(const Glib::RefPtr<Gio::File>& file, std::vector<FileName>& dirs, const Glib::ustring& prefix) {
361     Glib::RefPtr<Gio::FileEnumerator> child_enumeration =
362 	file->enumerate_children(G_FILE_ATTRIBUTE_STANDARD_NAME
363 				 "," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
364     Glib::RefPtr<Gio::FileInfo> file_info;
365     while ((file_info = child_enumeration->next_file())) {
366 	if (file_info->get_file_type() == Gio::FILE_TYPE_DIRECTORY) {
367 	    Glib::RefPtr<Gio::File> child = file->get_child(
368 		file_info->get_attribute_byte_string(G_FILE_ATTRIBUTE_STANDARD_NAME));
369 	    dirs.push_back(
370 		FileName(
371 		    child->get_path(),
372 		    prefix+file_info->get_attribute_string(G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME)));
373 	    list_subdirs(child, dirs, prefix+"  ");
374 	}
375     }
376 }
377 
list_subdirs(PathList pl,std::vector<FileName> & dirs)378 void list_subdirs(PathList pl, std::vector<FileName>& dirs) {
379     for (PathList::iterator i = pl.begin(); i != pl.end(); ++i) {
380 	std::string fn = (*i)->get_path();
381 	dirs.push_back(FileName(fn, fn));
382 	list_subdirs(*i, dirs, "  ");
383     }
384 }
385 
386 
387 /****************************************************************
388  ** class BasicOptions
389  */
390 
391 BasicOptions *BasicOptions::instance = 0;
392 
BasicOptions()393 BasicOptions::BasicOptions()
394     : user_dir(),
395       user_IR_dir(),
396       sys_IR_dir(GX_SOUND_DIR),
397       IR_pathlist(),
398       IR_prefixmap(),
399     builder_dir(GX_BUILDER_DIR) {
400     user_dir = Glib::build_filename(Glib::get_user_config_dir(), "guitarix");
401     user_IR_dir = Glib::build_filename(user_dir, "IR");
402 
403     make_ending_slash(user_dir);
404     make_ending_slash(user_IR_dir);
405     make_ending_slash(sys_IR_dir);
406     make_ending_slash(builder_dir);
407 
408     // for legacy presets
409     IR_pathlist.add(get_user_IR_dir());
410     IR_pathlist.add(get_sys_IR_dir());
411 
412     // for current presets
413     IR_prefixmap.add('U', get_user_IR_dir());
414     IR_prefixmap.add('S', get_sys_IR_dir());
415 
416     instance = this;
417 }
418 
~BasicOptions()419 BasicOptions::~BasicOptions() {
420     instance = 0;
421 }
422 
make_ending_slash(string & dirpath)423 void BasicOptions::make_ending_slash(string& dirpath) {
424     if (dirpath.empty()) {
425 	return;
426     }
427     if (dirpath[dirpath.size()-1] != '/') {
428 	dirpath += "/";
429     }
430 }
431 
432 
433 /****************************************************************
434  ** class CmdlineOptions
435  */
436 
shellvar(const char * name)437 static inline const char *shellvar(const char *name) {
438     const char *p = getenv(name);
439     return p ? p : "";
440 }
441 
442 #define TCLR(s)  "\033[1;32m" s "\033[0m" // light green
443 #define TCLR2(s) TCLR(s), s
444 
CmdlineOptions()445 CmdlineOptions::CmdlineOptions()
446     : BasicOptions(),
447       main_group("",""),
448       optgroup_style("style", TCLR2("GTK style configuration options")),
449       optgroup_jack("jack", TCLR2("JACK configuration options")),
450       optgroup_overload("overload", TCLR2("Switch to bypass mode on overload condition")),
451       optgroup_file("file", TCLR2("File options")),
452       optgroup_debug("debug", TCLR2("Debug options")),
453       version(false), clear(false),
454       jack_input(shellvar("GUITARIX2JACK_INPUTS")),
455       jack_midi(shellvar("GUITARIX2JACK_MIDI")),
456       jack_outputs(),
457       jack_uuid(),
458       jack_uuid2(),
459       jack_noconnect(false),
460       jack_single(false),
461       jack_servername(),
462       load_file(shellvar("GUITARIX_LOAD_FILE")),
463       style_dir(GX_STYLE_DIR),
464       factory_dir(GX_FACTORY_DIR),
465       pixmap_dir(GX_PIXMAPS_DIR),
466       old_user_dir(),
467       preset_dir(),
468       pluginpreset_dir(),
469       lv2_preset_dir(),
470       temp_dir(),
471       plugin_dir(),
472       loop_dir(),
473       rcset(shellvar("GUITARIX_RC_STYLE")),
474       nogui(false),
475       rpcport(RPCPORT_DEFAULT),
476       rpcaddress(),
477       onlygui(false),
478       liveplaygui(false),
479       hideonquit(false),
480       mute(false),
481       setbank(),
482       cmdline_bank(),
483       cmdline_preset(),
484       tuner_tet(),
485       tuner_ref(),
486       sporadic_overload(0),
487       idle_thread_timeout(0),
488       convolver_watchdog(true),
489       watchdog_warning(true),
490       xrun_watchdog(false),
491       lterminal(false),
492       a_save(false),
493       auto_save(false),
494 #ifndef NDEBUG
495       dump_parameter(false),
496 #endif
497       skin(style_dir),
498       mainwin_visible(0),
499       mainwin_x(-1),
500       mainwin_y(-1),
501       mainwin_height(-1),
502       window_height(600),
503       preset_window_height(220),
504       mul_buffer(1),
505       no_warn_latency(false),
506       system_order_rack_h(false),
507       system_show_value(false),
508       system_show_tooltips(true),
509       system_animations(true),
510       system_show_presets(false),
511       system_show_toolbar(false),
512       system_show_rack(false),
513       system_midiout(false),
514       reload_lv2_presets(true) {
515     const char* home = getenv("HOME");
516     if (!home) {
517 	throw GxFatalError(_("no HOME environment variable"));
518     }
519     old_user_dir = string(home) + "/.gx_head/";
520     plugin_dir = Glib::build_filename(get_user_dir(), "plugins");
521     preset_dir = Glib::build_filename(get_user_dir(), "banks");
522     pluginpreset_dir = Glib::build_filename(get_user_dir(), "pluginpresets");
523     lv2_preset_dir = Glib::build_filename(get_user_dir(), "pluginpresets/lv2");
524     loop_dir = Glib::build_filename(get_pluginpreset_dir(), "loops");
525     temp_dir = Glib::build_filename(get_user_dir(), "temp");
526     const char *tmp = getenv("GUITARIX2JACK_OUTPUTS1");
527     if (tmp && *tmp) {
528 	jack_outputs.push_back(tmp);
529     }
530     tmp = getenv("GUITARIX2JACK_OUTPUTS2");
531     if (tmp && *tmp) {
532 	jack_outputs.push_back(tmp);
533     }
534 
535     const char *nsm_url = getenv( "NSM_URL" );
536 
537     if (!nsm_url) {
538         read_ui_vars();
539     }
540 
541     // ---- parse command line arguments
542     set_summary(
543         "All parameters are optional. Examples:\n"
544         "\tguitarix\n"
545         "\tguitarix -r gx4-black -i system:capture_3\n"
546         "\tguitarix -c -o system:playback_1 -o system:playback_2");
547 
548     // main group
549     Glib::OptionEntry opt_version;
550     opt_version.set_short_name('v');
551     opt_version.set_long_name("version");
552     opt_version.set_description("Print version string and exit");
553     Glib::OptionEntry opt_nogui;
554     opt_nogui.set_short_name('N');
555     opt_nogui.set_long_name("nogui");
556     opt_nogui.set_description("start without GUI");
557     Glib::OptionEntry opt_rpcport;
558     opt_rpcport.set_short_name('p');
559     opt_rpcport.set_long_name("rpcport");
560     opt_rpcport.set_description("start a JSON-RPC server listening on port PORT");
561     opt_rpcport.set_arg_description("PORT");
562     Glib::OptionEntry opt_rpchost;
563     opt_rpchost.set_short_name('H');
564     opt_rpchost.set_long_name("rpchost");
565     opt_rpchost.set_description("set hostname to connect to");
566     opt_rpchost.set_arg_description("HOSTNAME");
567     Glib::OptionEntry opt_onlygui;
568     opt_onlygui.set_short_name('G');
569     opt_onlygui.set_long_name("onlygui");
570     opt_onlygui.set_description("start only GUI");
571     Glib::OptionEntry opt_liveplaygui;
572     opt_liveplaygui.set_short_name('L');
573     opt_liveplaygui.set_long_name("liveplaygui");
574     opt_liveplaygui.set_description("start with Live Play GUI");
575     Glib::OptionEntry opt_hideonquit;
576     opt_hideonquit.set_short_name('E');
577     opt_hideonquit.set_long_name("hideonquit");
578     opt_hideonquit.set_description("only hide GUI instead quit engine");
579     Glib::OptionEntry opt_mute;
580     opt_mute.set_short_name('M');
581     opt_mute.set_long_name("mute");
582     opt_mute.set_description("start with engine muted");
583     Glib::OptionEntry opt_bank;
584     opt_bank.set_short_name('b');
585     opt_bank.set_long_name("bank");
586     opt_bank.set_description("set bank and preset to load at startup");
587     opt_bank.set_arg_description("BANK:PRESET (A:0-Z:9)");
588     Glib::OptionEntry opt_tuner_tet;
589     opt_tuner_tet.set_short_name('t');
590     opt_tuner_tet.set_long_name("tuner_tet");
591     opt_tuner_tet.set_description("set tuner temperament at startup");
592     opt_tuner_tet.set_arg_description("tuner temperament (12, 19, 24, 31, 41, 53)");
593     Glib::OptionEntry opt_tuner_ref;
594     opt_tuner_ref.set_short_name('F');
595     opt_tuner_ref.set_long_name("reference_pitch");
596     opt_tuner_ref.set_description("set tuner reference pitch at startup");
597     opt_tuner_ref.set_arg_description("tuner reference pitch (225 - 453)");
598     Glib::OptionEntry opt_tuner_feedback;
599     opt_tuner_feedback.set_long_name("tuner-midi-feedback");
600     opt_tuner_feedback.set_description("send tuner midi feedback");
601     main_group.add_entry(opt_version, version);
602     main_group.add_entry(opt_nogui, nogui);
603     main_group.add_entry(opt_rpcport, rpcport);
604     main_group.add_entry(opt_rpchost, rpcaddress);
605     main_group.add_entry(opt_onlygui, onlygui);
606     main_group.add_entry(opt_liveplaygui, liveplaygui);
607     main_group.add_entry(opt_hideonquit, hideonquit);
608     main_group.add_entry(opt_mute, mute);
609     main_group.add_entry(opt_bank, setbank);
610     main_group.add_entry(opt_tuner_tet, tuner_tet);
611     main_group.add_entry(opt_tuner_ref, tuner_ref);
612     main_group.add_entry(opt_tuner_feedback, system_tuner_midiout);
613     set_main_group(main_group);
614 
615     // style options
616     Glib::OptionEntry opt_clear;
617     opt_clear.set_short_name('c');
618     opt_clear.set_long_name("clear");
619     opt_clear.set_description("Use 'default' GTK style");
620     Glib::OptionEntry opt_rcset;
621     opt_rcset.set_short_name('r');
622     opt_rcset.set_long_name("rcset");
623     opt_rcset.set_description(get_opskin());
624     opt_rcset.set_arg_description("STYLE");
625     optgroup_style.add_entry(opt_clear, clear);
626     optgroup_style.add_entry(opt_rcset, rcset);
627 
628     // JACK options
629     Glib::OptionEntry opt_jack_input;
630     opt_jack_input.set_short_name('i');
631     opt_jack_input.set_long_name("jack-input");
632     opt_jack_input.set_description("Guitarix JACK input");
633     opt_jack_input.set_arg_description("PORT");
634     Glib::OptionEntry opt_jack_output;
635     opt_jack_output.set_short_name('o');
636     opt_jack_output.set_long_name("jack-output");
637     opt_jack_output.set_description("Guitarix JACK outputs");
638     opt_jack_output.set_arg_description("PORT");
639     Glib::OptionEntry opt_jack_midi;
640     opt_jack_midi.set_short_name('m');
641     opt_jack_midi.set_long_name("jack-midi");
642     opt_jack_midi.set_description("Guitarix JACK midi control");
643     opt_jack_midi.set_arg_description("PORT");
644     Glib::OptionEntry opt_jack_midi_feedback;
645     opt_jack_midi_feedback.set_long_name("jack-midi-feedback");
646     opt_jack_midi_feedback.set_description("Guitarix send JACK midi feedback");
647     Glib::OptionEntry opt_jack_noconnect;
648     opt_jack_noconnect.set_short_name('J');
649     opt_jack_noconnect.set_long_name("jack-no-connect");
650     opt_jack_noconnect.set_description("disable self-connect JACK ports");
651     Glib::OptionEntry opt_jack_instance;
652     opt_jack_instance.set_short_name('n');
653     opt_jack_instance.set_long_name("name");
654     opt_jack_instance.set_description("instance name (default gx_head)");
655     opt_jack_instance.set_arg_description("NAME");
656     Glib::OptionEntry opt_jack_single;
657     opt_jack_single.set_short_name('D');
658     opt_jack_single.set_long_name("disable-multi-client");
659     opt_jack_single.set_description("run guitarix as single client");
660     Glib::OptionEntry opt_jack_uuid;
661     opt_jack_uuid.set_short_name('U');
662     opt_jack_uuid.set_long_name("jack-uuid");
663     opt_jack_uuid.set_description("JackSession ID");
664     opt_jack_uuid.set_arg_description("UUID");
665     Glib::OptionEntry opt_jack_uuid2;
666     opt_jack_uuid2.set_short_name('A');
667     opt_jack_uuid2.set_long_name("jack-uuid2");
668     opt_jack_uuid2.set_description("JackSession ID");
669     opt_jack_uuid2.set_arg_description("UUID2");
670     Glib::OptionEntry opt_jack_servername;
671     opt_jack_servername.set_short_name('s');
672     opt_jack_servername.set_long_name("server-name");
673     opt_jack_servername.set_description("JACK server name to connect to");
674     opt_jack_servername.set_arg_description("NAME");
675     optgroup_jack.add_entry(opt_jack_input, jack_input);
676     optgroup_jack.add_entry(opt_jack_output, jack_outputs);
677     optgroup_jack.add_entry(opt_jack_midi, jack_midi);
678     optgroup_jack.add_entry(opt_jack_midi_feedback, system_midiout);
679     optgroup_jack.add_entry(opt_jack_noconnect, jack_noconnect);
680     optgroup_jack.add_entry(opt_jack_instance, jack_instance);
681     optgroup_jack.add_entry(opt_jack_single, jack_single);
682     optgroup_jack.add_entry(opt_jack_uuid, jack_uuid);
683     optgroup_jack.add_entry(opt_jack_uuid2, jack_uuid2);
684     optgroup_jack.add_entry(opt_jack_servername, jack_servername);
685 
686     // Engine overload options
687     Glib::OptionEntry opt_watchdog_idle;
688     opt_watchdog_idle.set_short_name('I');
689     opt_watchdog_idle.set_long_name("idle-timeout");
690     opt_watchdog_idle.set_description(
691 	"starved idle thread probe (default: disabled)");
692     opt_watchdog_idle.set_arg_description("SECONDS");
693     Glib::OptionEntry opt_watchdog_convolver;
694     opt_watchdog_convolver.set_short_name('C');
695     opt_watchdog_convolver.set_long_name("no-convolver-overload");
696     opt_watchdog_convolver.set_description(
697 	"disable overload on convolver missed deadline");
698     opt_watchdog_convolver.set_flags(Glib::OptionEntry::FLAG_REVERSE);
699     Glib::OptionEntry opt_watchdog_warning;
700     opt_watchdog_warning.set_short_name('W');
701     opt_watchdog_warning.set_long_name("no-watchdog-warning");
702     opt_watchdog_warning.set_description(
703 	"do not pop-up warning for bypassed overload condition");
704     opt_watchdog_warning.set_flags(Glib::OptionEntry::FLAG_REVERSE);
705     Glib::OptionEntry opt_watchdog_xrun;
706     opt_watchdog_xrun.set_short_name('X');
707     opt_watchdog_xrun.set_long_name("xrun-overload");
708     opt_watchdog_xrun.set_description(
709 	"JACK xrun (default: false)");
710     Glib::OptionEntry opt_sporadic_overload;
711     opt_sporadic_overload.set_short_name('S');
712     opt_sporadic_overload.set_long_name("sporadic");
713     opt_sporadic_overload.set_description(
714 	"allow single overload events per interval (default: disabled)");
715     opt_sporadic_overload.set_arg_description("SECONDS");
716     optgroup_overload.add_entry(opt_watchdog_idle, idle_thread_timeout);
717     optgroup_overload.add_entry(opt_watchdog_convolver, convolver_watchdog);
718     optgroup_overload.add_entry(opt_watchdog_warning, watchdog_warning);
719     optgroup_overload.add_entry(opt_watchdog_xrun, xrun_watchdog);
720     optgroup_overload.add_entry(opt_sporadic_overload, sporadic_overload);
721 
722     // FILE options
723     Glib::OptionEntry opt_load_file;
724     opt_load_file.set_short_name('f');
725     opt_load_file.set_long_name("load-file");
726     opt_load_file.set_description(_("load state file on startup"));
727     opt_load_file.set_arg_description("FILE");
728     optgroup_file.add_entry_filename(opt_load_file, load_file);
729     Glib::OptionEntry opt_plugin_dir;
730     opt_plugin_dir.set_short_name('P');
731     opt_plugin_dir.set_long_name("plugin-dir");
732     opt_plugin_dir.set_description(_("directory with guitarix plugins (.so files)"));
733     opt_plugin_dir.set_arg_description("DIR");
734     optgroup_file.add_entry_filename(opt_plugin_dir, plugin_dir);
735     Glib::OptionEntry opt_save_on_exit;
736     opt_save_on_exit.set_short_name('K');
737     opt_save_on_exit.set_long_name("disable-save-on-exit");
738     opt_save_on_exit.set_description(_("disable auto save to state file when quit"));
739     optgroup_file.add_entry(opt_save_on_exit, a_save);
740     Glib::OptionEntry opt_auto_save;
741     opt_auto_save.set_short_name('a');
742     opt_auto_save.set_long_name("auto-save");
743     opt_auto_save.set_description(_("enable auto save (only in server mode)"));
744     optgroup_file.add_entry(opt_auto_save, auto_save);
745 
746     // DEBUG options
747     Glib::OptionEntry opt_builder_dir;
748     opt_builder_dir.set_short_name('B');
749     opt_builder_dir.set_long_name("builder-dir");
750     opt_builder_dir.set_description(_("directory from which .glade files are loaded"));
751     opt_builder_dir.set_arg_description("DIR");
752     optgroup_debug.add_entry_filename(opt_builder_dir, builder_dir);
753     Glib::OptionEntry opt_style_dir;
754     opt_style_dir.set_short_name('S');
755     opt_style_dir.set_long_name("style-dir");
756     opt_style_dir.set_description(_("directory with skin style definitions (.css files)"));
757     opt_style_dir.set_arg_description("DIR");
758     optgroup_debug.add_entry_filename(opt_style_dir, style_dir);
759     Glib::OptionEntry opt_log_terminal;
760     opt_log_terminal.set_short_name('t');
761     opt_log_terminal.set_long_name("log-terminal");
762     opt_log_terminal.set_description(_("print log on terminal"));
763     optgroup_debug.add_entry(opt_log_terminal, lterminal);
764 #ifndef NDEBUG
765     Glib::OptionEntry opt_dump_parameter;
766     opt_dump_parameter.set_short_name('d');
767     opt_dump_parameter.set_long_name("dump-parameter");
768     opt_dump_parameter.set_description(_("dump parameter table in json format"));
769     optgroup_debug.add_entry(opt_dump_parameter, dump_parameter);
770 #endif
771 
772     // collecting all option groups
773     add_group(optgroup_style);
774     add_group(optgroup_jack);
775     add_group(optgroup_overload);
776     add_group(optgroup_file);
777     add_group(optgroup_debug);
778 }
779 
~CmdlineOptions()780 CmdlineOptions::~CmdlineOptions() {
781     write_ui_vars();
782 }
783 
read_ui_vars()784 void CmdlineOptions::read_ui_vars() {
785     ifstream i(Glib::build_filename(get_user_dir(), "ui_rc").c_str());
786     if (i.fail()) {
787         return;
788     }
789     JsonParser jp(&i);
790     try {
791         jp.next(JsonParser::begin_object);
792         while (jp.peek() != JsonParser::end_object) {
793             jp.next(JsonParser::value_key);
794             if (jp.current_value() == "system.mainwin_visible") {
795                 jp.next(JsonParser::value_number);
796                 mainwin_visible = jp.current_value_int();
797             } else if (jp.current_value() == "system.mainwin_x") {
798                 jp.next(JsonParser::value_number);
799                 mainwin_x = jp.current_value_int();
800             } else if (jp.current_value() == "system.mainwin_y") {
801                 jp.next(JsonParser::value_number);
802                 mainwin_y = jp.current_value_int();
803             } else if (jp.current_value() == "system.mainwin_height") {
804                 jp.next(JsonParser::value_number);
805                 mainwin_height = jp.current_value_int();
806             } else if (jp.current_value() == "system.mainwin_rack_height") {
807                 jp.next(JsonParser::value_number);
808                 window_height = jp.current_value_int();
809             } else if (jp.current_value() == "system.preset_window_height") {
810                 jp.next(JsonParser::value_number);
811                 preset_window_height = jp.current_value_int();
812             } else if (jp.current_value() == "system.mul_buffer") {
813                 jp.next(JsonParser::value_number);
814                 mul_buffer = jp.current_value_int();
815             } else if (jp.current_value() == "ui.skin_name") {
816                 jp.next(JsonParser::value_string);
817                 if (skin.is_in_list(jp.current_value())) {
818                     skin.name = jp.current_value();
819                 } else {
820                     gx_print_error(
821                         _("load state"),
822                         Glib::ustring::compose(_("Skin '%1' not found"), jp.current_value()));
823                 }
824             } else if (jp.current_value() == "ui.latency_nowarn") {
825                 jp.next(JsonParser::value_number);
826                 no_warn_latency = jp.current_value_int();
827             } else if (jp.current_value() == "system.order_rack_h") {
828                 jp.next(JsonParser::value_number);
829                 system_order_rack_h = jp.current_value_int();
830             } else if (jp.current_value() == "system.show_value") {
831                 jp.next(JsonParser::value_number);
832                 system_show_value = jp.current_value_int();
833             } else if (jp.current_value() == "system.show_tooltips") {
834                 jp.next(JsonParser::value_number);
835                 system_show_tooltips = jp.current_value_int();
836             } else if (jp.current_value() == "system.animations") {
837                 jp.next(JsonParser::value_number);
838                 system_animations = jp.current_value_int();
839             } else if (jp.current_value() == "system.show_presets") {
840                 jp.next(JsonParser::value_number);
841                 system_show_presets = jp.current_value_int();
842             } else if (jp.current_value() == "system.show_toolbar") {
843                 jp.next(JsonParser::value_number);
844                 system_show_toolbar = jp.current_value_int();
845             } else if (jp.current_value() == "system.show_rack") {
846                 jp.next(JsonParser::value_number);
847                 system_show_rack = jp.current_value_int();
848             } else if (jp.current_value() == "system.midiout") {
849                 jp.next(JsonParser::value_number);
850                 system_midiout = jp.current_value_int();
851             }
852         }
853         jp.next(JsonParser::end_object);
854         jp.close();
855     } catch (JsonException&) {
856         gx_print_warning("main", "can't read/parse ui_rc");
857     }
858     i.close();
859 }
860 
write_ui_vars()861 void CmdlineOptions::write_ui_vars() {
862     ofstream o(Glib::build_filename(get_user_dir(), "ui_rc").c_str());
863     if (o.fail()) {
864 	return;
865     }
866     JsonWriter jw(&o);
867     try {
868 	jw.begin_object(true);
869 	jw.write_kv("system.mainwin_visible", mainwin_visible);
870 	jw.write_kv("system.mainwin_x", mainwin_x);
871 	jw.write_kv("system.mainwin_y", mainwin_y);
872 	jw.write_kv("system.mainwin_height", mainwin_height);
873 	jw.write_kv("system.mainwin_rack_height", window_height);
874 	jw.write_kv("system.preset_window_height", preset_window_height);
875 	jw.write_kv("system.mul_buffer", mul_buffer);
876 	jw.write_kv("ui.skin_name", skin.name);
877 	jw.write_kv("ui.latency_nowarn", no_warn_latency);
878 	jw.write_kv("system.order_rack_h", system_order_rack_h);
879 	jw.write_kv("system.show_value", system_show_value);
880 	jw.write_kv("system.show_tooltips", system_show_tooltips);
881 	jw.write_kv("system.animations", system_animations);
882 	jw.write_kv("system.show_presets", system_show_presets);
883 	jw.write_kv("system.show_toolbar", system_show_toolbar);
884 	jw.write_kv("system.show_rack", system_show_rack);
885 	jw.write_kv("system.midiout", system_midiout);
886 	jw.end_object(true);
887 	jw.close();
888     } catch (JsonException&) {
889 	gx_print_warning("main", "can't write ui_rc");
890     }
891     o.close();
892 }
893 
get_jack_output(unsigned int n) const894 Glib::ustring CmdlineOptions::get_jack_output(unsigned int n) const {
895     if (n >= jack_outputs.size()) {
896 	return "";
897     }
898     return jack_outputs.at(n);
899 }
900 
get_opskin()901 string CmdlineOptions::get_opskin() {
902     // GTK options: rc style (aka skin)
903     string opskin("Style to use");
904 
905     // initialize number of skins. We just count the number of rc files
906     unsigned int n = skin.skin_list.size();
907     if (n < 1) {
908         return opskin;
909     }
910 
911     vector<Glib::ustring>::iterator it;
912 
913     for (it = skin.skin_list.begin(); it != skin.skin_list.end(); ++it) {
914         opskin += ", '" + *it + "'";
915     }
916     return opskin;
917 }
918 
log_terminal(const string & msg,GxLogger::MsgType tp,bool plugged)919 static void log_terminal(const string& msg, GxLogger::MsgType tp, bool plugged) {
920     const char *t;
921     switch (tp) {
922     case GxLogger::kInfo:    t = "I"; break;
923     case GxLogger::kWarning: t = "W"; break;
924     case GxLogger::kError:   t = "E"; break;
925     default:       t = "?"; break;
926     }
927     if (!plugged) {
928 	cerr << t << " " << msg << endl;
929     }
930 }
931 
process(int argc,char ** argv)932 void CmdlineOptions::process(int argc, char** argv) {
933     path_to_program = Gio::File::create_for_path(argv[0])->get_path();
934     if (version) {
935         std::cout << "Guitarix version \033[1;32m"
936              << GX_VERSION << endl
937              << "\033[0m   Copyright " << static_cast<char>(0x40) << " 2010 "
938              << "Hermman Meyer - James Warden - Andreas Degert"
939              << endl;
940         exit(0);
941     }
942 #ifdef NDEBUG
943     if (argc > 1) {
944 	throw GxFatalError(
945 	    string("unknown argument on command line: ")+argv[1]);
946     }
947 #endif
948     if (clear) {
949 	if (!rcset.empty()) {
950 	    throw Glib::OptionError(
951 		Glib::OptionError::BAD_VALUE,
952 		_("-c and -r cannot be used together"));
953 	}
954 	skin.name = "";
955     } else if (skin.name.empty()) {
956 	skin.set_default_skin_name();
957     }
958     if (nogui && liveplaygui) {
959 		throw Glib::OptionError(
960 	    Glib::OptionError::BAD_VALUE,
961 	    _("-N and -L cannot be used together"));
962 	}
963     if (onlygui && !setbank.empty()) {
964 		throw Glib::OptionError(
965 	    Glib::OptionError::BAD_VALUE,
966 	    _("-G and -b cannot be used together"));
967 	}
968     if (lterminal) {
969 	GxLogger::get_logger().signal_message().connect(
970 	    sigc::ptr_fun(log_terminal));
971 	if (nogui) {
972 	    GxLogger::get_logger().unplug_queue();
973 	}
974     }
975 
976     make_ending_slash(builder_dir);
977     make_ending_slash(style_dir);
978     make_ending_slash(factory_dir);
979     make_ending_slash(pixmap_dir);
980     make_ending_slash(preset_dir);
981     make_ending_slash(pluginpreset_dir);
982     make_ending_slash(lv2_preset_dir);
983     make_ending_slash(loop_dir);
984     make_ending_slash(temp_dir);
985     make_ending_slash(plugin_dir);
986 
987     skin.set_styledir(style_dir);
988     unsigned int n = skin.skin_list.size();
989     if (n < 1) {
990         gx_print_fatal(_("main"), string(_("number of skins is 0")));
991     }
992     if (!rcset.empty()) {
993 	if (skin.is_in_list(rcset)) {
994 	    skin.name = rcset;
995 	} else {
996 	    throw Glib::OptionError(
997 		Glib::OptionError::BAD_VALUE,
998 		(boost::format(_("invalid style '%1%' on command line"))
999 		 % rcset).str());
1000 	}
1001     }
1002     if (jack_outputs.size() > 2) {
1003 	gx_print_warning(
1004 	    _("main"),
1005 	    _("Warning --> provided more than 2 output ports, ignoring extra ports"));
1006     }
1007 }
1008 
1009 
1010 /****************************************************************
1011  ** misc functions
1012  */
1013 
1014 // ---- gx_head system function
gx_system_call(const string & cmd,const bool devnull,const bool escape)1015 int gx_system_call(const string& cmd,
1016                    const bool  devnull,
1017                    const bool  escape) {
1018     string str = cmd;
1019 
1020     if (devnull)
1021         str.append(" 1>/dev/null 2>&1");
1022 
1023     if (escape)
1024         str.append("&");
1025 
1026     //    cerr << " ********* \n system command = " << str.c_str() << endl;
1027 
1028     sigset_t waitset;
1029     sigemptyset(&waitset);
1030     sigaddset(&waitset, SIGCHLD);
1031     sigprocmask(SIG_UNBLOCK, &waitset, NULL);
1032     int rc = system(str.c_str());
1033     sigprocmask(SIG_BLOCK, &waitset, NULL);
1034     return rc;
1035 }
1036 
strip(Glib::ustring & s)1037 void strip(Glib::ustring& s) {
1038     size_t n = s.find_first_not_of(' ');
1039     if (n == Glib::ustring::npos) {
1040 	s.erase();
1041 	return;
1042     }
1043     if (n != 0) {
1044 	s.erase(0, n);
1045     }
1046     s.erase(s.find_last_not_of(' ')+1);
1047 }
1048 
1049 /*
1050 ** encoding / decoding filenames
1051 */
1052 
check_char(unsigned char c)1053 static inline bool check_char(unsigned char c) {
1054     static const char *badchars = "/%?*<>\\:#&$'\"(){}[]~;`|.";
1055     if (c < 32) {
1056 	return false;
1057     }
1058     for (const char *p = badchars; *p; p++) {
1059 	if (c == *p) {
1060 	    return false;
1061 	}
1062     }
1063     return true;
1064 }
1065 
encode_filename(const std::string & s)1066 std::string encode_filename(const std::string& s) {
1067    std::string res;
1068    res.reserve(s.size());
1069    for (unsigned int i = 0; i < s.size(); i++) {
1070       unsigned char c = s[i];
1071       if (!check_char(c)) {
1072 	 res.append(1, '%');
1073 	 static const unsigned char code[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1074 	 res.append(1, code[c / 16]);
1075 	 res.append(1, code[c % 16]);
1076       } else {
1077 	 res.append(1, c);
1078       }
1079    }
1080    return res;
1081 }
1082 
dct(unsigned char c,int & n)1083 static inline bool dct(unsigned char c, int &n) {
1084    if (c < '0') {
1085       return false;
1086    }
1087    if (c <= '9') {
1088       n = c - '0';
1089       return true;
1090    }
1091    if (c < 'a') {
1092       return false;
1093    }
1094    if (c <= 'f') {
1095       n = c - 'a';
1096       return true;
1097    }
1098    return false;
1099 }
1100 
decode_filename(const std::string & s)1101 std::string decode_filename(const std::string& s) {
1102    std::string res;
1103    res.reserve(s.size());
1104    for (unsigned int i = 0; i < s.size(); i++) {
1105       unsigned char c = s[i];
1106       if (c == '%') {
1107 	 int n1, n2;
1108 	 if (s.size() - i < 3 || !dct(s[i+1],n1) || !dct(s[i+2],n2)) {
1109 	    // error, don't do any decoding
1110 	    return s;
1111 	 }
1112 	 res.push_back(n1*16 + n2);
1113 	 i += 2;
1114       } else {
1115 	 res.push_back(c);
1116       }
1117    }
1118    return res;
1119 }
1120 
1121 
1122 } /* end of gx_system namespace */
1123