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  *
21  *    This is gx_head main.
22  *
23  * ----------------------------------------------------------------------------
24  */
25 
26 #include "guitarix.h"       // NOLINT
27 
28 #include <gtkmm/main.h>     // NOLINT
29 #include <gxwmm/init.h>     // NOLINT
30 #include <string>           // NOLINT
31 #include <thread>
32 
33 #include "jsonrpc.h"
34 
35 #ifdef HAVE_AVAHI
36 #include "avahi_discover.h"
37 #endif
38 
39 /****************************************************************
40  ** class NsmSignals
41  **
42  ** signal messages from GxNsmHandler to MainWindow
43  */
44 
NsmSignals()45 NsmSignals::NsmSignals()
46     : nsm_session_control(false) {
47 }
48 
49 
50 /****************************************************************
51  ** class GxNsmHandler
52  **
53  ** watch for messages from NSM
54  */
55 
56 #ifdef HAVE_LIBLO
57 
58 class GxNsmHandler {
59   private:
60     gx_system::CmdlineOptions *options;
61     NsmSignals *nsmsig;
62     int _nsm_open (const char *name, const char *display_name,
63             const char *client_id, char **out_msg);
64     int _nsm_save ( char **out_msg);
65     void _nsm_show ();
66     void _nsm_hide ();
67     void _on_nsm_is_shown();
68     void _on_nsm_is_hidden();
69     void _nsm_start_poll();
70     bool _poll_nsm();
71     nsm_client_t *nsm;
72     bool wait_id;
73 
74   public:
75     bool check_nsm(char *argv[]);
76     static int gx_nsm_open (const char *name, const char *display_name,
77             const char *client_id, char **out_msg,  void *userdata);
78     static int gx_nsm_save ( char **out_msg, void *userdata );
79     static void gx_nsm_show ( void *userdata );
80     static void gx_nsm_hide ( void *userdata );
81     GxNsmHandler(gx_system::CmdlineOptions *options,NsmSignals *nsmsig);
82     ~GxNsmHandler();
83 };
84 
GxNsmHandler(gx_system::CmdlineOptions * options_,NsmSignals * nsmsig_)85 GxNsmHandler::GxNsmHandler(gx_system::CmdlineOptions *options_,NsmSignals *nsmsig_)
86     : options(options_),
87       nsmsig(nsmsig_),
88       nsm(0),
89       wait_id(true) {
90     nsmsig->signal_trigger_nsm_gui_is_shown().connect(
91         sigc::mem_fun(this, &::GxNsmHandler::_on_nsm_is_shown));
92 
93     nsmsig->signal_trigger_nsm_gui_is_hidden().connect(
94         sigc::mem_fun(this, &GxNsmHandler::_on_nsm_is_hidden));
95 }
96 
~GxNsmHandler()97 GxNsmHandler::~GxNsmHandler() {
98     if (nsm) {
99         nsm_free( nsm );
100         nsm = 0;
101     }
102 }
103 
check_nsm(char * argv[])104 bool GxNsmHandler::check_nsm(char *argv[]) {
105 
106     const char *nsm_url = getenv( "NSM_URL" );
107 
108     if (nsm_url) {
109         nsm = nsm_new();
110         nsm_set_open_callback( nsm, gx_nsm_open, static_cast<void*>(this));
111         nsm_set_save_callback( nsm, gx_nsm_save, static_cast<void*>(this));
112         nsm_set_show_callback( nsm, gx_nsm_show, static_cast<void*>(this));
113         nsm_set_hide_callback( nsm, gx_nsm_hide, static_cast<void*>(this));
114         if ( 0 == nsm_init( nsm, nsm_url)) {
115             nsm_send_announce( nsm, "Guitarix", ":optional-gui:", argv[0]);
116             int wait_count = 0;
117             while(wait_id) {
118                 nsm_check_wait(nsm,500);
119                 wait_count +=1;
120                 if (wait_count > 200) {
121                     gx_gui::gx_message_popup("NSM didn't response in time, \nGuitarix will exit now");
122                     GxExit::get_instance().exit_program("NSM didn't response, exit now");
123                 }
124             }
125             return true;
126         } else {
127             nsm_free(nsm);
128             nsm = 0;
129         }
130     }
131     return false;
132 }
133 
_poll_nsm()134 bool GxNsmHandler::_poll_nsm() {
135     if (nsm) {
136         nsm_check_nowait(nsm);
137         return true;
138     }
139     return false;
140 }
141 
_nsm_start_poll()142 void GxNsmHandler::_nsm_start_poll() {
143     Glib::signal_timeout().connect(
144         sigc::mem_fun(*this, &GxNsmHandler::_poll_nsm),
145         200, Glib::PRIORITY_LOW);
146 }
147 
_nsm_open(const char * name,const char * display_name,const char * client_id,char ** out_msg)148 int GxNsmHandler::_nsm_open (const char *name, const char *display_name,
149                             const char *client_id, char **out_msg ) {
150 
151     std::string dirpath = name;
152     if (dirpath[dirpath.size()-1] != '/') {
153         dirpath += "/";
154     }
155 
156     options->set_user_dir(dirpath);
157     options->set_user_IR_dir( Glib::build_filename(dirpath, "IR/"));
158     options->set_preset_dir( Glib::build_filename(dirpath, "banks/"));
159     options->set_plugin_dir( Glib::build_filename(dirpath, "plugins/"));
160     options->set_pluginpreset_dir( Glib::build_filename(dirpath, "pluginpresets/"));
161     options->set_lv2_preset_dir( Glib::build_filename(dirpath, "pluginpresets/lv2/"));
162     options->set_loop_dir( Glib::build_filename(dirpath, "loops/"));
163     options->set_temp_dir( Glib::build_filename(dirpath, "temp/"));
164 
165     options->set_hideonquit(true);
166     options->set_jack_instancename(client_id);
167     options->set_jack_noconnect(true);
168     options->set_jack_single(true);
169    // options->set_opt_autosave(true);
170     options->read_ui_vars();
171     if (!strstr(nsm_get_session_manager_features(nsm), ":optional-gui:")) {
172         options->mainwin_visible = 1;
173         gx_print_info(_("nsm startup"), _("optional-gui not supported by server"));
174     }
175     wait_id = false;
176     gx_print_info(_("nsm startup"), name);
177     _nsm_start_poll();
178 
179     return ERR_OK;
180 }
181 
_nsm_save(char ** out_msg)182 int GxNsmHandler::_nsm_save ( char **out_msg) {
183     if (gx_preset::GxSettings::instance && nsm) {
184         bool cur_state = gx_preset::GxSettings::instance->get_auto_save_state();
185         gx_preset::GxSettings::instance->disable_autosave(false);
186         gx_preset::GxSettings::instance->auto_save_state();
187         gx_preset::GxSettings::instance->disable_autosave(cur_state);
188         nsmsig->trigger_nsm_save_gui();
189     }
190     return ERR_OK;
191 }
192 
_nsm_show()193 void GxNsmHandler::_nsm_show () {
194     nsmsig->trigger_nsm_show_gui();
195 }
196 
_nsm_hide()197 void GxNsmHandler::_nsm_hide () {
198     nsmsig->trigger_nsm_hide_gui();
199 }
200 
_on_nsm_is_shown()201 void GxNsmHandler::_on_nsm_is_shown () {
202     nsm_send_is_shown(nsm);
203 }
204 
_on_nsm_is_hidden()205 void GxNsmHandler::_on_nsm_is_hidden () {
206     nsm_send_is_hidden(nsm);
207 }
208 
209 //static
gx_nsm_open(const char * name,const char * display_name,const char * client_id,char ** out_msg,void * userdata)210 int GxNsmHandler::gx_nsm_open (const char *name, const char *display_name,
211             const char *client_id, char **out_msg, void *userdata ) {
212     GxNsmHandler * nsmhandler = static_cast<GxNsmHandler*>(userdata);
213     return nsmhandler->_nsm_open (name, display_name, client_id, out_msg);
214 }
215 
216 //static
gx_nsm_save(char ** out_msg,void * userdata)217 int GxNsmHandler::gx_nsm_save ( char **out_msg, void *userdata ) {
218     GxNsmHandler * nsmhandler = static_cast<GxNsmHandler*>(userdata);
219     return nsmhandler->_nsm_save(out_msg);
220 }
221 
222 //static
gx_nsm_show(void * userdata)223 void GxNsmHandler::gx_nsm_show (void *userdata ) {
224     GxNsmHandler * nsmhandler = static_cast<GxNsmHandler*>(userdata);
225     return nsmhandler->_nsm_show();
226 }
227 
228 //static
gx_nsm_hide(void * userdata)229 void GxNsmHandler::gx_nsm_hide (void *userdata ) {
230     GxNsmHandler * nsmhandler = static_cast<GxNsmHandler*>(userdata);
231     return nsmhandler->_nsm_hide();
232 }
233 
234 #endif
235 
236 /****************************************************************
237  ** class GxTheme
238  */
239 
init(gx_system::CmdlineOptions * options_)240 void GxTheme::init(gx_system::CmdlineOptions* options_) {
241     options = options_;
242     if (!options) {
243         return; // program exit, no cleanup needed
244     }
245     std::string st_dir = options->get_style_dir();
246     Gtk::IconTheme::get_default()->prepend_search_path(st_dir.substr(0, st_dir.size()-1));
247     css_provider = Gtk::CssProvider::create();
248     css_show_values = Gtk::CssProvider::create();
249     style_context = Gtk::StyleContext::create();
250     style_context->add_provider_for_screen(    Gdk::Screen::get_default(), css_show_values,
251         GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
252     style_context->add_provider_for_screen(    Gdk::Screen::get_default(), css_provider,
253         GTK_STYLE_PROVIDER_PRIORITY_APPLICATION+1);
254 }
255 
set_new_skin(const Glib::ustring & skin_name)256 bool GxTheme::set_new_skin(const Glib::ustring& skin_name) {
257     if (!options) {
258         return false;
259     }
260     if (skin_name.empty()) {
261         options->skin.name = skin_name;
262     } else {
263         Glib::RefPtr<Gtk::IconTheme> deftheme = Gtk::IconTheme::get_default();
264         std::vector<Glib::ustring> pathlist = deftheme->get_search_path();
265         if (pathlist.empty() || pathlist.front() != options->get_style_filepath(options->skin.name)) {
266             pathlist.insert(pathlist.cbegin(), "");
267         }
268         options->skin.name = skin_name;
269         *pathlist.begin() = options->get_style_filepath(skin_name);
270         deftheme->set_search_path(pathlist);
271         deftheme->rescan_if_needed();
272     }
273     css_provider->load_from_path(options->get_current_style_cssfile());
274     return true;
275 }
276 
update_show_values()277 void GxTheme::update_show_values() {
278     /* Gtk 3.24 uses information about which style modifications
279      * change widget sizes this doesn't work with the (deprecated)
280      * style setting show-value as work-around use a fake min-width
281      * setting and remove it after the update
282      *
283      * DON'T USE "min-width: 1px" for GxRegler derived widgets in the
284      * css style sheet, it will disable this update hack.
285      */
286     if (!options) {
287         return;
288     }
289     boost::format fmt(
290         "gx-regler, gx-big-knob, gx-mid-knob, gx-small-knob, gx-small-knob-r, gx-value-display, gx-vslider, gx-hslider {\n"
291         "  -GxRegler-show-value: %1%;\n%2%"
292         "}\n");
293     fmt % gx_system::to_string(options->system_show_value);
294     css_show_values->load_from_data((boost::format(fmt) % "  min-width: 1px\n").str());
295     for (int i = 0; i < 50; i++) { // doesn't work reliable without this wait loop
296         while (Gtk::Main::events_pending()) {
297             Gtk::Main::iteration(false);
298         }
299         usleep(1000);
300     }
301     css_show_values->load_from_data((fmt % "").str());
302 }
303 
304 #ifndef NDEBUG
reload_css_post()305 void GxTheme::reload_css_post() {
306     window->show();
307     window->move(window_x, window_y);
308     window->set_focus_on_map(true);
309     try {
310         css_provider->load_from_path(options->get_current_style_cssfile());
311     } catch (Gtk::CssProviderError& e) {
312         cerr << "CSS Style Error: " << e.what() << endl;
313         gx_gui::show_error_msg(e.what());
314     }
315     style_context->add_provider_for_screen(    Gdk::Screen::get_default(), css_provider,
316         GTK_STYLE_PROVIDER_PRIORITY_APPLICATION+1);
317 }
318 
reload_css()319 void GxTheme::reload_css() {
320     if (!options) {
321         return;
322     }
323     style_context->remove_provider_for_screen(Gdk::Screen::get_default(), css_provider);
324     Glib::signal_idle().connect_once(sigc::mem_fun(this, &GxTheme::reload_css_post));
325     window->get_position(window_x, window_y);
326     window->set_focus_on_map(false);
327     window->hide();
328 }
329 #endif
330 
331 
332 /****************************************************************
333  ** class PosixSignals
334  **
335  ** Block unix signals and catch them in a special thread.
336  ** Blocking is inherited by all threads created after an
337  ** instance of PosixSignals
338  */
339 
340 class PosixSignals {
341 private:
342     sigset_t waitset;
343     std::thread *thread;
344     bool gui;
345     GxTheme *theme;
346     volatile bool exit;
347     void signal_helper_thread();
348     void quit_slot();
349     void gx_ladi_handler();
350     void create_thread();
351     bool gtk_level();
352     static void relay_sigchld(int);
353 
354 public:
355     PosixSignals(bool gui, GxTheme *theme = nullptr);
356     ~PosixSignals();
357 };
358 
359 
PosixSignals(bool gui_,GxTheme * theme_)360 PosixSignals::PosixSignals(bool gui_, GxTheme *theme_)
361     : waitset(),
362       thread(nullptr),
363       gui(gui_),
364       theme(theme_),
365       exit(false) {
366     GxExit::get_instance().set_ui_thread();
367     sigemptyset(&waitset);
368     /* ----- block signal USR1 ---------
369     ** inherited by all threads which are created later
370     ** signals are processed synchronously by signal_helper_thread
371     */
372     sigaddset(&waitset, SIGUSR1);
373     if (theme) {
374         sigaddset(&waitset, SIGUSR2);
375     }
376     sigaddset(&waitset, SIGCHLD);
377     sigaddset(&waitset, SIGINT);
378     sigaddset(&waitset, SIGQUIT);
379     sigaddset(&waitset, SIGTERM);
380     sigaddset(&waitset, SIGHUP);
381     sigaddset(&waitset, SIGKILL);
382 
383     // ----- leave alone these signals: generated by programming errors
384     // SIGABRT
385     // SIGSEGV
386 
387     sigprocmask(SIG_BLOCK, &waitset, NULL);
388     create_thread();
389     signal(SIGCHLD, relay_sigchld);
390 }
391 
~PosixSignals()392 PosixSignals::~PosixSignals() {
393     if (thread) {
394         exit = true;
395         pthread_kill(thread->native_handle(), SIGINT);
396         thread->join();
397         delete thread;
398     }
399     sigprocmask(SIG_UNBLOCK, &waitset, NULL);
400 }
401 
create_thread()402 void PosixSignals::create_thread() {
403     try {
404         thread = new std::thread(
405             sigc::mem_fun(*this, &PosixSignals::signal_helper_thread));
406     } catch (std::system_error& e) {
407         throw GxFatalError(
408             boost::format(_("Thread create failed (signal): %1%")) % e.what());
409     }
410 }
411 
quit_slot()412 void PosixSignals::quit_slot() {
413     GxExit::get_instance().exit_program();
414 }
415 
gx_ladi_handler()416 void PosixSignals::gx_ladi_handler() {
417     gx_print_warning(
418         _("signal_handler"), _("signal USR1 received, save settings"));
419     if (gx_preset::GxSettings::instance) {
420         bool cur_state = gx_preset::GxSettings::instance->get_auto_save_state();
421         gx_preset::GxSettings::instance->disable_autosave(false);
422         gx_preset::GxSettings::instance->auto_save_state();
423         gx_preset::GxSettings::instance->disable_autosave(cur_state);
424     }
425 }
426 
relay_sigchld(int)427 void PosixSignals::relay_sigchld(int) {
428     kill(getpid(), SIGCHLD);
429 }
430 
gtk_level()431 bool PosixSignals::gtk_level() {
432     if (! gui) {
433         return 1;
434     } else {
435         return Gtk::Main::level();
436     }
437 }
438 
439 // --- wait for USR1 signal to arrive and invoke ladi handler via mainloop
signal_helper_thread()440 void PosixSignals::signal_helper_thread() {
441     const char *signame;
442     guint source_id_usr1 = 0;
443 #ifndef NDEBUG
444     guint source_id_usr2 = 0;
445 #endif
446     pthread_sigmask(SIG_BLOCK, &waitset, NULL);
447     bool seen = false;
448     while (true) {
449         int sig;
450         int ret = sigwait(&waitset, &sig);
451         if (exit) {
452             break;
453         }
454         if (ret != 0) {
455             assert(errno == EINTR);
456             continue;
457         }
458         switch (sig) {
459         case SIGUSR1:
460             if (gtk_level() < 1) {
461                 gx_print_info(_("system startup"),
462                         _("signal usr1 skipped"));
463             break;
464             }
465             // do not add a new call if another one is already pending
466             if (source_id_usr1 == 0 ||
467                 g_main_context_find_source_by_id(NULL, source_id_usr1) == NULL) {
468                     const Glib::RefPtr<Glib::IdleSource> idle_source = Glib::IdleSource::create();
469                     idle_source->connect( sigc::bind_return<bool>(
470                         sigc::mem_fun(*this, &PosixSignals::gx_ladi_handler),false));
471                     idle_source->attach();
472                     source_id_usr1 = idle_source->get_id();
473                 }
474             break;
475 #ifndef NDEBUG
476         case SIGUSR2:
477             if (gtk_level() < 1) {
478                 gx_print_info(_("system startup"),
479                         _("signal usr2 skipped"));
480                 break;
481             }
482             // do not add a new call if another one is already pending
483             if (source_id_usr2 == 0 ||
484                 g_main_context_find_source_by_id(NULL, source_id_usr2) == NULL) {
485                     const Glib::RefPtr<Glib::IdleSource> idle_source = Glib::IdleSource::create();
486                 idle_source->connect(
487                 sigc::bind_return<bool>(
488                 sigc::mem_fun(*theme, &GxTheme::reload_css), false));
489                 idle_source->attach();
490                 source_id_usr2 = idle_source->get_id();
491             }
492             break;
493 #endif
494         case SIGCHLD:
495             Glib::signal_idle().connect_once(
496                 sigc::ptr_fun(gx_child_process::gx_sigchld_handler));
497             break;
498         case SIGINT:
499         case SIGQUIT:
500         case SIGTERM:
501         case SIGHUP:
502             switch (sig) {
503             case SIGINT:
504                 signame = _("ctrl-c");
505                 break;
506             case SIGQUIT:
507                 signame = "SIGQUIT";
508                 break;
509             case SIGTERM:
510                 signame = "SIGTERM";
511                 break;
512             case SIGHUP:
513                 signame = "SIGHUP";
514                 break;
515             }
516             if (!seen && gtk_level() == 1) {
517                 printf("\nquit (%s)\n", signame);
518                 Glib::signal_idle().connect_once(sigc::mem_fun(*this, &PosixSignals::quit_slot));
519             } else {
520                 GxExit::get_instance().exit_program(
521                     (boost::format("\nQUIT (%1%)\n") % signame).str());
522             }
523             seen = true;
524             break;
525         default:
526             assert(false);
527         }
528     }
529 }
530 
531 
532 /****************************************************************
533  ** class ErrorPopup
534  ** show UI popup for kError messages
535  */
536 
537 class ErrorPopup {
538 private:
539     Glib::ustring msg;
540     bool active;
541     Gtk::MessageDialog *dialog;
542     void show_msg();
543     void on_response(int);
544 public:
545     ErrorPopup();
546     ~ErrorPopup();
547     void on_message(const Glib::ustring& msg, GxLogger::MsgType tp, bool plugged);
548 };
549 
ErrorPopup()550 ErrorPopup::ErrorPopup()
551     : msg(),
552       active(false),
553       dialog(0) {
554 }
555 
~ErrorPopup()556 ErrorPopup::~ErrorPopup() {
557     delete dialog;
558 }
559 
on_message(const Glib::ustring & msg_,GxLogger::MsgType tp,bool plugged)560 void ErrorPopup::on_message(const Glib::ustring& msg_, GxLogger::MsgType tp, bool plugged) {
561     if (plugged) {
562         return;
563     }
564     if (tp == GxLogger::kError) {
565         if (active) {
566             msg += "\n" + msg_;
567             if (msg.size() > 1000) {
568                 msg.substr(msg.size()-1000);
569             }
570             if (dialog) {
571                 dialog->set_message(msg);
572             }
573         } else {
574             msg = msg_;
575             active = true;
576             show_msg();
577         }
578     }
579 }
580 
on_response(int)581 void ErrorPopup::on_response(int) {
582     delete dialog;
583     dialog = 0;
584     active = false;
585 }
586 
show_msg()587 void ErrorPopup::show_msg() {
588     dialog = new Gtk::MessageDialog(msg, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE);
589     dialog->set_keep_above(true);
590     dialog->set_title(_("GUITARIX ERROR"));
591     dialog->signal_response().connect(
592         sigc::mem_fun(*this, &ErrorPopup::on_response));
593     dialog->show();
594 }
595 
596 
597 /****************************************************************
598  ** class GxSplashBox
599  ** show splash screen at start up
600  */
601 
602 class GxSplashBox: public Gtk::Window {
603 private:
604     virtual void on_show();
605     virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);
606     Cairo::RefPtr<Cairo::ImageSurface> image;
607 public:
608     explicit GxSplashBox(const string& imgpath);
~GxSplashBox()609     ~GxSplashBox() {}
610     virtual void on_message(const Glib::ustring& msg_, GxLogger::MsgType tp, bool plugged);
611 };
612 
GxSplashBox(const string & imgpath)613 GxSplashBox::GxSplashBox(const string& imgpath)
614     : Gtk::Window(Gtk::WINDOW_POPUP),
615       image(Cairo::ImageSurface::create_from_png(imgpath)) {
616     set_app_paintable();
617     set_position(Gtk::WIN_POS_CENTER);
618     set_default_size(image->get_width(), image->get_height());
619     realize();
620 #if GTK_MINOR_VERSION >= 24
621     shape_combine_region(Gdk::Cairo::create_region_from_surface(image));
622 #endif
623     show();
624 }
625 
on_draw(const Cairo::RefPtr<Cairo::Context> & cr)626 bool GxSplashBox::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
627     cr->set_source(image, 0, 0);
628     cr->paint();
629     return false;
630 }
631 
on_message(const Glib::ustring & msg_,GxLogger::MsgType tp,bool plugged)632 void GxSplashBox::on_message(const Glib::ustring& msg_, GxLogger::MsgType tp, bool plugged) {
633     Gtk::Widget::hide();
634 }
635 
on_show()636 void GxSplashBox::on_show() {
637     Gtk::Widget::on_show();
638     while(Gtk::Main::events_pending())
639         Gtk::Main::iteration(false);
640 }
641 
642 /****************************************************************
643  ** main()
644  */
645 
646 #if 0
647 #ifndef NDEBUG
648 int debug_display_glade(gx_engine::GxEngine& engine, gx_system::CmdlineOptions& options,
649                         gx_engine::ParamMap& pmap, const string& fname) {
650     pmap.set_init_values();
651     if (!options.get_rcset().empty()) {
652         std::string rcfile = options.get_style_filepath("gx_head_"+options.get_rcset()+".rc");
653         gtk_rc_parse(rcfile.c_str());
654         gtk_rc_reset_styles(gtk_settings_get_default());
655     }
656     Gtk::Window *w = 0;
657     gx_ui::GxUI ui;
658     Glib::RefPtr<gx_gui::GxBuilder> bld = gx_gui::GxBuilder::create_from_file(fname, &machine);
659     w = bld->get_first_window();
660     gx_ui::GxUI::updateAllGuis(true);
661     if (w) {
662     Gtk::Main::run(*w);
663     delete w;
664     }
665     return 0;
666 }
667 #endif
668 #endif
669 
670 #ifdef NDEBUG
671 // switch off GTK warnings in Release build
null_handler(const char * log_domain,GLogLevelFlags log_level,const gchar * msg,gpointer user_data)672 static void null_handler(const char *log_domain, GLogLevelFlags log_level,
673                          const gchar *msg, gpointer user_data ) {
674     return ;
675 }
676 #endif
677 
mainHeadless(int argc,char * argv[])678 static void mainHeadless(int argc, char *argv[]) {
679     Glib::init();
680     Gio::init();
681 
682     PosixSignals posixsig(false); // catch unix signals in special thread
683     gx_system::CmdlineOptions options;
684     options.parse(argc, argv);
685     options.process(argc, argv);
686     // ---------------- Check for working user directory  -------------
687     bool need_new_preset;
688     if (gx_preset::GxSettings::check_settings_dir(options, &need_new_preset)) {
689         cerr <<
690             _("old config directory found (.gx_head)."
691             " state file and standard presets file have been copied to"
692             " the new directory (.config/guitarix).\n"
693             " Additional old preset files can be imported into the"
694             " new bank scheme by mouse drag and drop with a file"
695             " manager");
696         return;
697     }
698 
699     gx_engine::GxMachine machine(options);
700 
701     gx_jack::GxJack::rt_watchdog_set_limit(options.get_idle_thread_timeout());
702     machine.loadstate();
703     //if (!in_session) {
704     //    gx_settings.disable_autosave(options.get_opt_auto_save());
705     //}
706 
707     if (! machine.get_jack()->gx_jack_connection(true, true, 0, options)) {
708         cerr << "can't connect to jack\n";
709         return;
710     }
711     if (need_new_preset) {
712         machine.create_default_scratch_preset();
713     }
714     // when midiout is requested we need to reload state in order to send midi feedback
715     if (options.system_midiout) machine.loadstate();
716     if (options.system_tuner_midiout) machine.set_parameter_value("system.midiout_tuner", true);
717     // ----------------------- Run Glib main loop ----------------------
718     cout << "Ctrl-C to quit\n";
719     Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create();
720     machine.get_jack()->shutdown.connect(sigc::mem_fun(loop.operator->(),&Glib::MainLoop::quit));
721     int port = options.get_rpcport();
722     if (port == RPCPORT_DEFAULT) {
723         port = 7000;
724     }
725     if (port != RPCPORT_NONE) {
726         machine.start_socket(sigc::mem_fun(loop.operator->(),&Glib::MainLoop::quit), options.get_rpcaddress(), port);
727         loop->run();
728     } else {
729         loop->run();
730     }
731     gx_child_process::childprocs.killall();
732 }
733 
exception_handler()734 static void exception_handler() {
735     try {
736         throw; // re-throw current exception
737     } catch (const GxFatalError& error) {
738         cerr << error.what() << endl;
739         gx_print_fatal(_("Guitarix fatal error"), error.what());
740     } catch (const Glib::OptionError &error) {
741         cerr << error.what() << endl;
742         cerr << _("use \"guitarix -h\" to get a help text") << endl;
743         gx_print_fatal(_("Guitarix Commandline Option Error"),
744                 Glib::ustring::compose(
745                 "%1\n%2",
746                 error.what(),
747                 _("use \"guitarix -h\" to get a help text")));
748     } catch (const Glib::Error& error) {
749         const GError *perr = error.gobj();
750         Glib::ustring msg = Glib::ustring::compose(
751             "Glib::Error[%1/%2]: %3",
752             g_quark_to_string(perr->domain),
753             perr->code,
754             (perr->message) ? perr->message : "(null)");
755         cerr << msg << endl;
756         gx_print_fatal(_("Guitarix fatal error"), msg);
757     } catch (const std::exception& except) {
758         Glib::ustring msg = Glib::ustring::compose(
759             "std::exception: %1", except.what());
760         cerr << msg << endl;
761         gx_print_fatal(_("Guitarix fatal error"), msg);
762     } catch(...) {
763         cerr << _("unknown error") << endl;
764         gx_print_fatal(_("Guitarix fatal error"),_("unknown error"));
765     }
766 }
767 
is_headless(int argc,char * argv[])768 static bool is_headless(int argc, char *argv[]) {
769     for (int i = 0; i < argc; ++i) {
770         if (strcmp(argv[i], "-N") == 0 || strcmp(argv[i], "--nogui") == 0) {
771             return true;
772         }
773     }
774     return false;
775 }
776 
is_frontend(int argc,char * argv[])777 static bool is_frontend(int argc, char *argv[]) {
778     for (int i = 0; i < argc; ++i) {
779         if (strcmp(argv[i], "-G") == 0 || strcmp(argv[i], "--onlygui") == 0) {
780             return true;
781         }
782     }
783     return false;
784 }
785 
mainGtk(gx_system::CmdlineOptions & options,NsmSignals & nsmsig,GxTheme & theme,GxSplashBox * Splash,bool need_new_preset)786 static void mainGtk(gx_system::CmdlineOptions& options, NsmSignals& nsmsig, GxTheme& theme, GxSplashBox *Splash, bool need_new_preset) {
787     gx_engine::GxMachine machine(options);
788     while(Gtk::Main::events_pending()) {
789         // prevents crash in show_error_msg!dialog.run() when its
790         // called due to an early exception (like some icon file not
791         // found when the style-dir is bad). This is probably not the
792         // correct cure but it helps...
793         Gtk::Main::iteration(false);
794     }
795 #if 0
796 #ifndef NDEBUG
797     if (argc > 1) {
798         delete Splash;
799         debug_display_glade(engine, options, gx_engine::parameter_map, argv[1]);
800         return;
801     }
802 #endif
803 #endif
804     // ----------------------- init GTK interface----------------------
805     MainWindow gui(machine, options, nsmsig, theme, Splash, "");
806     if (need_new_preset) {
807         gui.create_default_scratch_preset();
808     }
809     // when midiout is requested we need to reload state in order to send midi feedback
810     if (options.system_midiout) machine.loadstate();
811     if (options.system_tuner_midiout) machine.set_parameter_value("system.midiout_tuner", true);
812     delete Splash;
813     gui.run();
814 }
815 
mainFront(gx_system::CmdlineOptions & options,NsmSignals & nsmsig,GxTheme & theme,GxSplashBox * Splash,bool need_new_preset)816 static void mainFront(gx_system::CmdlineOptions& options, NsmSignals& nsmsig, GxTheme& theme, GxSplashBox *Splash, bool need_new_preset) {
817     Glib::ustring title;
818 #ifdef HAVE_AVAHI
819     if (options.get_rpcaddress().empty() && options.get_rpcport() == RPCPORT_DEFAULT) {
820         SelectInstance si(options, Splash);
821         if (Splash) {
822             Splash->show();
823         }
824         Glib::ustring a;
825         int port;
826         Glib::ustring name;
827         Glib::ustring host;
828         if (!si.get_address_port(a, port, name, host)) {
829             cerr << "Failed to get address" << endl;
830             return;
831         }
832         options.set_rpcaddress(a);
833         options.set_rpcport(port);
834         title = Glib::ustring::compose("%1 / %2:%3", name, host, port);
835     }
836 #endif // HAVE_AVAHI
837     if (options.get_rpcport() == RPCPORT_DEFAULT) {
838         options.set_rpcport(7000);
839     }
840     if (options.get_rpcaddress().empty()) {
841         options.set_rpcaddress("localhost");
842     }
843     if (title.empty()) {
844         title = Glib::ustring::compose("%1:%2", options.get_rpcaddress(), options.get_rpcport());
845     }
846     gx_engine::GxMachineRemote machine(options);
847 
848     // ----------------------- init GTK interface----------------------
849     MainWindow gui(machine, options, nsmsig,theme, Splash, title);
850     if (need_new_preset) {
851         gui.create_default_scratch_preset();
852     }
853     machine.set_init_values();
854     delete Splash;
855     gui.run();
856 }
857 
mainProg(int argc,char * argv[])858 static void mainProg(int argc, char *argv[]) {
859     bool frontend = is_frontend(argc, argv);
860     Glib::init();
861 
862     GxTheme theme;
863 #ifndef NDEBUG
864     PosixSignals posixsig(true, &theme); // catch unix signals in special thread
865 #else
866     PosixSignals posixsig(true, nullptr);
867 #endif
868     Glib::add_exception_handler(sigc::ptr_fun(exception_handler));
869     gx_system::CmdlineOptions options;
870 
871     NsmSignals nsmsig;
872 
873     Gtk::Main main(argc, argv, options);
874     Gxw::init();
875     options.process(argc, argv);
876 
877 #ifdef HAVE_LIBLO
878     GxNsmHandler nsmhandler(&options, &nsmsig);
879     nsmsig.nsm_session_control = nsmhandler.check_nsm(argv);
880 #endif
881 
882     bool theme_ok = false;
883     if (options.get_clear_rc()) {
884         options.skin.name = "";
885     } else if (options.skin.name.empty()) {
886         options.skin.name = "Guitarix";
887     }
888     theme.init(&options);
889     try { // early theme init
890         theme.set_new_skin(options.skin.name);
891         theme_ok = false;
892     } catch (...) {
893     // try again later to display message dialog
894     }
895     GxSplashBox * Splash = NULL;
896 #ifdef NDEBUG
897     Splash =  new GxSplashBox(options.get_pixmap_filepath("gx_splash.png"));
898     GxLogger::get_logger().signal_message().connect(
899     sigc::mem_fun(Splash, &GxSplashBox::on_message));
900     g_log_set_handler("Gtk",G_LOG_LEVEL_WARNING,null_handler,NULL);
901 #endif
902     GxExit::get_instance().signal_msg().connect(
903     sigc::ptr_fun(gx_gui::show_error_msg));  // show fatal errors in UI
904     ErrorPopup popup;
905     GxLogger::get_logger().signal_message().connect(
906     sigc::mem_fun(popup, &ErrorPopup::on_message));
907     if (!theme_ok) {
908         theme.set_new_skin(options.skin.name); // try again, display error message
909     }
910     // ---------------- Check for working user directory  -------------
911     bool need_new_preset;
912     if (gx_preset::GxSettings::check_settings_dir(options, &need_new_preset)) {
913         if (Splash) {
914             Splash->hide();
915         }
916         Gtk::MessageDialog dialog(
917             _("old config directory found (.gx_head)."
918               " state file and standard presets file have been copied to"
919               " the new directory (.config/guitarix).\n"
920               " Additional old preset files can be imported into the"
921               " new bank scheme by mouse drag and drop with a file"
922               " manager"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true);
923         dialog.set_title("Guitarix");
924         dialog.run();
925     }
926 
927     if (frontend) {
928         mainFront(options, nsmsig, theme, Splash, need_new_preset);
929     } else {
930         mainGtk(options, nsmsig, theme, Splash, need_new_preset);
931     }
932 
933     gx_child_process::childprocs.killall();
934     theme.init(nullptr);
935 }
936 
main(int argc,char * argv[])937 int main(int argc, char *argv[]) {
938     // This is a hack. If we start qjackctl, we want to make sure it scale
939     // automatically for HiDPI displays.
940     // Note: we don't change then environment if it is already set to *any* value.
941     if (!getenv("QT_AUTO_SCREEN_SCALE_FACTOR")) {
942         setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1", 0);
943     }
944 #ifdef DISABLE_NLS
945 // break
946 #elif defined(IS_MACOSX)
947 // break
948 #elif ENABLE_NLS
949     bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
950     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
951     textdomain(GETTEXT_PACKAGE);
952 #endif
953 
954     try {
955     // ----------------------- init basic subsystems ----------------------
956 #if (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32)
957     if (!Glib::thread_supported()) {
958         Glib::thread_init();
959     }
960 #endif
961     if (is_headless(argc, argv)) {
962         mainHeadless(argc, argv);
963     } else {
964         mainProg(argc, argv);
965     }
966     } catch (...) {
967         exception_handler();
968     }
969     return 0;
970 }
971