summaryrefslogtreecommitdiffstats
path: root/src/gui/ConnectWindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/ConnectWindow.cpp')
-rw-r--r--src/gui/ConnectWindow.cpp458
1 files changed, 458 insertions, 0 deletions
diff --git a/src/gui/ConnectWindow.cpp b/src/gui/ConnectWindow.cpp
new file mode 100644
index 00000000..5600a0b2
--- /dev/null
+++ b/src/gui/ConnectWindow.cpp
@@ -0,0 +1,458 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string>
+#include <time.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <raul/Process.hpp>
+#include CONFIG_H_PATH
+#include "interface/EngineInterface.hpp"
+#include "module/World.hpp"
+#include "engine/tuning.hpp"
+#include "engine/Engine.hpp"
+#include "engine/QueuedEngineInterface.hpp"
+#include "client/OSCClientReceiver.hpp"
+#include "client/HTTPClientReceiver.hpp"
+#include "client/OSCEngineSender.hpp"
+#include "client/ThreadedSigClientInterface.hpp"
+#include "client/ClientStore.hpp"
+#include "client/PatchModel.hpp"
+#include "module/Module.hpp"
+#include "App.hpp"
+#include "WindowFactory.hpp"
+#include "ConnectWindow.hpp"
+using Ingen::QueuedEngineInterface;
+using Ingen::Client::ThreadedSigClientInterface;
+using namespace std;
+
+namespace Ingen {
+namespace GUI {
+
+
+// ConnectWindow
+
+
+ConnectWindow::ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml)
+ : Gtk::Dialog(cobject)
+ , _xml(xml)
+ , _mode(CONNECT_REMOTE)
+ , _ping_id(-1)
+ , _attached(false)
+ , _widgets_loaded(false)
+ , _connect_stage(0)
+ , _new_engine(NULL)
+{
+}
+
+
+void
+ConnectWindow::start(Ingen::Shared::World* world)
+{
+ if (world->local_engine) {
+ _mode = INTERNAL;
+ if (_widgets_loaded)
+ _internal_radio->set_active(true);
+ }
+
+ set_connected_to(world->engine);
+
+ connect(true);
+}
+
+
+void
+ConnectWindow::set_connected_to(SharedPtr<Shared::EngineInterface> engine)
+{
+ App::instance().world()->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 (_new_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 EngineInterface and ClientInterface and initialize
+ * the App with them.
+ */
+void
+ConnectWindow::connect(bool existing)
+{
+ if (_attached)
+ _attached = false;
+
+ assert(!App::instance().client());
+
+ _connect_stage = 0;
+ set_connecting_widget_states();
+
+ Ingen::Shared::World* world = App::instance().world();
+
+ if (_mode == CONNECT_REMOTE) {
+ if (!existing) {
+ const string url = (_widgets_loaded ? (string)_url_entry->get_text() : world->engine->uri());
+ world->engine = SharedPtr<EngineInterface>(new OSCEngineSender(url));
+ }
+
+ SharedPtr<ThreadedSigClientInterface> tsci(new ThreadedSigClientInterface(1024));
+ SharedPtr<Raul::Deletable> client;
+
+ const string& uri = world->engine->uri();
+ const string& scheme = uri.substr(0, uri.find(":"));
+ if (scheme == "osc.udp" || scheme == "osc.tcp")
+ client = SharedPtr<OSCClientReceiver>(new OSCClientReceiver(16181, tsci)); // FIXME: port
+ else if (scheme == "http")
+ client = SharedPtr<HTTPClientReceiver>(new HTTPClientReceiver(world, uri, tsci));
+
+ App::instance().attach(tsci, client);
+ App::instance().register_callbacks();
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &ConnectWindow::gtk_callback), 40);
+
+ } else if (_mode == LAUNCH_REMOTE) {
+
+ int port = _port_spinbutton->get_value_as_int();
+ char port_str[6];
+ snprintf(port_str, 6, "%u", port);
+ const string cmd = string("ingen -e --engine-port=").append(port_str);
+
+ if (Raul::Process::launch(cmd)) {
+ world->engine = SharedPtr<EngineInterface>(
+ new OSCEngineSender(string("osc.udp://localhost:").append(port_str)));
+
+ // FIXME: static args
+ SharedPtr<ThreadedSigClientInterface> tsci(new ThreadedSigClientInterface(1024));
+ SharedPtr<OSCClientReceiver> client(new OSCClientReceiver(16181, tsci));
+
+ App::instance().attach(tsci, client);
+ App::instance().register_callbacks();
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &ConnectWindow::gtk_callback), 40);
+
+ } else {
+ cerr << "Failed to launch ingen process." << endl;
+ }
+
+ } else if (_mode == INTERNAL) {
+ Ingen::Shared::World* world = App::instance().world();
+ if ( ! world->local_engine) {
+ assert(_new_engine);
+ world->local_engine = SharedPtr<Engine>(_new_engine(world));
+ }
+
+ if ( ! world->engine)
+ world->engine = world->local_engine->new_queued_interface();
+
+ SharedPtr<SigClientInterface> client(new SigClientInterface());
+
+ world->local_engine->start_jack_driver();
+ world->local_engine->activate(1); // FIXME: parallelism
+
+ App::instance().attach(client);
+ App::instance().register_callbacks();
+
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &ConnectWindow::gtk_callback), 10);
+ }
+}
+
+
+void
+ConnectWindow::disconnect()
+{
+ _connect_stage = -1;
+ _attached = false;
+
+ App::instance().detach();
+ set_connected_to(SharedPtr<Ingen::Shared::EngineInterface>());
+
+ if (!_widgets_loaded)
+ return;
+
+ _progress_bar->set_fraction(0.0);
+ _connect_button->set_sensitive(false);
+ _disconnect_button->set_sensitive(false);
+
+ _connect_button->set_sensitive(true);
+ _disconnect_button->set_sensitive(false);
+}
+
+
+void
+ConnectWindow::on_show()
+{
+ if (!_widgets_loaded) {
+ load_widgets();
+ if (_attached)
+ set_connected_to(App::instance().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_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));
+ _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));
+
+ _progress_bar->set_pulse_step(0.01);
+ _widgets_loaded = true;
+
+ _engine_module = Ingen::Shared::load_module("ingen_engine");
+ if (!_engine_module)
+ cerr << "Unable to load ingen_engine module, internal engine unavailable." << endl;
+ bool found = _engine_module->get_symbol("new_engine", (void*&)_new_engine);
+ if (!found) {
+ cerr << "Unable to find module entry point, internal engine unavailable." << endl;
+ _engine_module.reset();
+ }
+
+ server_toggled();
+}
+
+
+void
+ConnectWindow::on_hide()
+{
+ Gtk::Dialog::on_hide();
+ if (!_attached)
+ Gtk::Main::quit();
+}
+
+
+void
+ConnectWindow::quit()
+{
+ if (_attached) {
+ Gtk::MessageDialog d(*this, "This will exit the GUI, but the engine will "
+ "remain running (if it is remote).\n\nAre you sure you want to quit?",
+ true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true);
+ d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ d.add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CLOSE);
+ int ret = d.run();
+ if (ret == Gtk::RESPONSE_CLOSE)
+ Gtk::Main::quit();
+ } else {
+ Gtk::Main::quit();
+ }
+}
+
+
+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 */
+
+ // 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();
+ }
+ }
+
+ /* Connecting to engine */
+ if (_connect_stage == 0) {
+
+ _attached = false;
+
+ assert(App::instance().engine());
+ assert(App::instance().client());
+
+ App::instance().client()->signal_response_ok.connect(
+ sigc::mem_fun(this, &ConnectWindow::on_response));
+
+ _ping_id = abs(rand()) / 2 * 2; // avoid -1
+ App::instance().engine()->set_next_response_id(_ping_id);
+ App::instance().engine()->ping();
+
+ 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 || App::instance().client()->enabled()) {
+ App::instance().engine()->activate();
+ ++_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::instance().engine()->set_next_response_id(_ping_id);
+ App::instance().engine()->ping();
+ last = now;
+ }
+ }
+ } else if (_connect_stage == 2) {
+ App::instance().engine()->request_all_objects();
+ if (_widgets_loaded)
+ _progress_label->set_text(string("Requesting root patch..."));
+ ++_connect_stage;
+ } else if (_connect_stage == 3) {
+ if (App::instance().store()->size() > 0) {
+ SharedPtr<PatchModel> root = PtrCast<PatchModel>(App::instance().store()->object("/"));
+ if (root) {
+ set_connected_to(App::instance().engine());
+ App::instance().window_factory()->present_patch(root);
+ App::instance().engine()->load_plugins();
+ if (_widgets_loaded)
+ _progress_label->set_text(string("Loading plugins..."));
+ ++_connect_stage;
+ }
+ }
+ } else if (_connect_stage == 4) {
+ App::instance().engine()->request_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)
+ 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;
+ }
+}
+
+
+} // namespace GUI
+} // namespace Ingen