/* This file is part of Ingen. Copyright 2007-2012 David Robillard Ingen is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. Ingen is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. You should have received a copy of the GNU Affero General Public License along with Ingen. If not, see . */ #include #include #include "raul/Process.hpp" #include "raul/log.hpp" #include "ingen_config.h" #include "ingen/EngineBase.hpp" #include "ingen/Interface.hpp" #include "ingen/client/ClientStore.hpp" #include "ingen/client/PatchModel.hpp" #include "ingen/client/ThreadedSigClientInterface.hpp" #include "ingen/shared/Module.hpp" #include "ingen/shared/World.hpp" #include "App.hpp" #include "ConnectWindow.hpp" #include "WindowFactory.hpp" using namespace Ingen::Client; using namespace std; using namespace Raul; namespace Raul { class Deletable; } namespace Ingen { namespace GUI { ConnectWindow::ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) : Dialog(cobject) , _xml(xml) , _mode(CONNECT_REMOTE) , _ping_id(-1) , _attached(false) , _finished_connecting(false) , _widgets_loaded(false) , _connect_stage(0) , _quit_flag(false) { } void ConnectWindow::start(App& app, Ingen::Shared::World* world) { _app = &app; if (world->local_engine()) { _mode = INTERNAL; if (_widgets_loaded) { _internal_radio->set_active(true); } } set_connected_to(world->engine()); connect(world->engine()); } void ConnectWindow::set_connected_to(SharedPtr engine) { _app->world()->set_engine(engine); if (!_widgets_loaded) return; if (engine) { _icon->set(Gtk::Stock::CONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); _progress_bar->set_fraction(1.0); _progress_label->set_text("Connected to engine"); _url_entry->set_sensitive(false); _connect_button->set_sensitive(false); _disconnect_button->set_label("gtk-disconnect"); _disconnect_button->set_sensitive(true); _port_spinbutton->set_sensitive(false); _launch_radio->set_sensitive(false); _internal_radio->set_sensitive(false); } else { _icon->set(Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); _progress_bar->set_fraction(0.0); _connect_button->set_sensitive(true); _disconnect_button->set_sensitive(false); if (_app->world()->local_engine()) _internal_radio->set_sensitive(true); else _internal_radio->set_sensitive(false); _server_radio->set_sensitive(true); _launch_radio->set_sensitive(true); if (_mode == CONNECT_REMOTE ) _url_entry->set_sensitive(true); else if (_mode == LAUNCH_REMOTE ) _port_spinbutton->set_sensitive(true); _progress_label->set_text(string("Disconnected")); } } void ConnectWindow::set_connecting_widget_states() { if (!_widgets_loaded) return; _connect_button->set_sensitive(false); _disconnect_button->set_label("gtk-cancel"); _disconnect_button->set_sensitive(true); _server_radio->set_sensitive(false); _launch_radio->set_sensitive(false); _internal_radio->set_sensitive(false); _url_entry->set_sensitive(false); _port_spinbutton->set_sensitive(false); } /** Launch (if applicable) and connect to the Engine. * * This will create the required interfaces and initialize the App with them. */ void ConnectWindow::connect(bool existing) { if (_attached) _attached = false; assert(!_app->client()); _connect_stage = 0; set_connecting_widget_states(); Ingen::Shared::World* world = _app->world(); #if defined(HAVE_LIBLO) || defined(HAVE_SOUP) if (_mode == CONNECT_REMOTE) { #ifdef HAVE_LIBLO string uri = "osc.udp://localhost:16180"; #else string uri = "http://localhost:16180"; #endif if (_widgets_loaded) { const std::string& user_uri = _url_entry->get_text(); if (Raul::URI::is_valid(user_uri)) uri = user_uri; } if (existing) uri = world->engine()->uri().str(); // Create client-side listener SharedPtr tsci(new ThreadedSigClientInterface(1024)); world->set_engine(world->interface(uri, tsci)); _app->attach(tsci); _app->register_callbacks(); Glib::signal_timeout().connect( sigc::mem_fun(this, &ConnectWindow::gtk_callback), 40); } else if (_mode == LAUNCH_REMOTE) { #ifdef HAVE_LIBLO int port = _port_spinbutton->get_value_as_int(); char port_str[8]; snprintf(port_str, sizeof(port_str), "%u", port); const string cmd = string("ingen -e -E ").append(port_str); if (Raul::Process::launch(cmd)) { const std::string engine_uri = string("osc.udp://localhost:").append(port_str); SharedPtr tsci(new ThreadedSigClientInterface(1024)); world->set_engine(world->interface(engine_uri, tsci)); _app->attach(tsci); _app->register_callbacks(); Glib::signal_timeout().connect( sigc::mem_fun(this, &ConnectWindow::gtk_callback), 40); } else { error << "Failed to launch ingen process." << endl; } #else error << "No OSC support" << endl; #endif } else #endif // defined(HAVE_LIBLO) || defined(HAVE_SOUP) if (_mode == INTERNAL) { if (!world->local_engine()) world->load_module("server"); SharedPtr client(new SigClientInterface()); world->load_module("jack"); world->local_engine()->activate(); _app->attach(client); _app->register_callbacks(); Glib::signal_timeout().connect( sigc::mem_fun(this, &ConnectWindow::gtk_callback), 10); } } void ConnectWindow::disconnect() { _connect_stage = -1; _attached = false; _app->detach(); set_connected_to(SharedPtr()); if (!_widgets_loaded) return; _activate_button->set_sensitive(false); _deactivate_button->set_sensitive(false); _progress_bar->set_fraction(0.0); _connect_button->set_sensitive(true); _disconnect_button->set_sensitive(false); } void ConnectWindow::activate() { _app->engine()->set_property("ingen:driver", "ingen:enabled", _app->forge().make(true)); } void ConnectWindow::deactivate() { _app->engine()->set_property("ingen:driver", "ingen:enabled", _app->forge().make(false)); } void ConnectWindow::on_show() { if (!_widgets_loaded) { load_widgets(); if (_attached) set_connected_to(_app->engine()); } Gtk::Dialog::on_show(); } void ConnectWindow::load_widgets() { _xml->get_widget("connect_icon", _icon); _xml->get_widget("connect_progress_bar", _progress_bar); _xml->get_widget("connect_progress_label", _progress_label); _xml->get_widget("connect_server_radiobutton", _server_radio); _xml->get_widget("connect_url_entry", _url_entry); _xml->get_widget("connect_launch_radiobutton", _launch_radio); _xml->get_widget("connect_port_spinbutton", _port_spinbutton); _xml->get_widget("connect_internal_radiobutton", _internal_radio); _xml->get_widget("connect_activate_button", _activate_button); _xml->get_widget("connect_deactivate_button", _deactivate_button); _xml->get_widget("connect_disconnect_button", _disconnect_button); _xml->get_widget("connect_connect_button", _connect_button); _xml->get_widget("connect_quit_button", _quit_button); _server_radio->signal_toggled().connect( sigc::mem_fun(this, &ConnectWindow::server_toggled)); _launch_radio->signal_toggled().connect( sigc::mem_fun(this, &ConnectWindow::launch_toggled)); _internal_radio->signal_clicked().connect( sigc::mem_fun(this, &ConnectWindow::internal_toggled)); _activate_button->signal_clicked().connect( sigc::mem_fun(this, &ConnectWindow::activate)); _deactivate_button->signal_clicked().connect( sigc::mem_fun(this, &ConnectWindow::deactivate)); _disconnect_button->signal_clicked().connect( sigc::mem_fun(this, &ConnectWindow::disconnect)); _connect_button->signal_clicked().connect( sigc::bind(sigc::mem_fun(this, &ConnectWindow::connect), false)); _quit_button->signal_clicked().connect( sigc::mem_fun(this, &ConnectWindow::quit_clicked)); _progress_bar->set_pulse_step(0.01); _widgets_loaded = true; server_toggled(); } void ConnectWindow::on_hide() { Gtk::Dialog::on_hide(); if (_app->window_factory()->num_open_patch_windows() == 0) quit(); } void ConnectWindow::quit_clicked() { if (_app->quit(this)) _quit_flag = true; } void ConnectWindow::server_toggled() { _url_entry->set_sensitive(true); _port_spinbutton->set_sensitive(false); _mode = CONNECT_REMOTE; } void ConnectWindow::launch_toggled() { _url_entry->set_sensitive(false); _port_spinbutton->set_sensitive(true); _mode = LAUNCH_REMOTE; } void ConnectWindow::internal_toggled() { _url_entry->set_sensitive(false); _port_spinbutton->set_sensitive(false); _mode = INTERNAL; } bool ConnectWindow::gtk_callback() { /* If I call this a "state machine" it's not ugly code any more */ if (_quit_flag) return false; // deregister this callback // Timing stuff for repeated attach attempts timeval now; gettimeofday(&now, NULL); static const timeval start = now; static timeval last = now; // Show if attempted connection goes on for a noticeable amount of time if (!is_visible()) { const float ms_since_start = (now.tv_sec - start.tv_sec) * 1000.0f + (now.tv_usec - start.tv_usec) * 0.001f; if (ms_since_start > 500) { present(); set_connecting_widget_states(); } } if (_connect_stage == 0) { _attached = false; _app->client()->signal_response().connect( sigc::mem_fun(this, &ConnectWindow::ingen_response)); _ping_id = abs(rand()) / 2 * 2; // avoid -1 _app->engine()->set_response_id(_ping_id); _app->engine()->get("ingen:engine"); if (_widgets_loaded) { _progress_label->set_text("Connecting to engine..."); _progress_bar->set_pulse_step(0.01); } ++_connect_stage; } else if (_connect_stage == 1) { if (_attached) { ++_connect_stage; } else { const float ms_since_last = (now.tv_sec - last.tv_sec) * 1000.0f + (now.tv_usec - last.tv_usec) * 0.001f; if (ms_since_last > 1000) { _app->engine()->set_response_id(_ping_id); _app->engine()->get("ingen:engine"); last = now; } } } else if (_connect_stage == 2) { _app->engine()->get(Path("/")); if (_widgets_loaded) _progress_label->set_text(string("Requesting root patch...")); ++_connect_stage; } else if (_connect_stage == 3) { if (_app->store()->size() > 0) { SharedPtr root = PtrCast( _app->store()->object("/")); if (root) { set_connected_to(_app->engine()); _app->window_factory()->present_patch(root); _app->engine()->get("ingen:plugins"); if (_widgets_loaded) _progress_label->set_text(string("Loading plugins...")); ++_connect_stage; } } } else if (_connect_stage == 4) { _app->engine()->get("ingen:plugins"); hide(); if (_widgets_loaded) _progress_label->set_text("Connected to engine"); _connect_stage = 0; // set ourselves up for next time (if there is one) _finished_connecting = true; return false; // deregister this callback } if (_widgets_loaded) _progress_bar->pulse(); if (_connect_stage == -1) { // we were cancelled if (_widgets_loaded) { _icon->set(Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); _progress_bar->set_fraction(0.0); _connect_button->set_sensitive(true); _disconnect_button->set_sensitive(false); _disconnect_button->set_label("gtk-disconnect"); _progress_label->set_text(string("Disconnected")); } return false; } else { return true; } } void ConnectWindow::quit() { _quit_flag = true; Gtk::Main::quit(); } } // namespace GUI } // namespace Ingen