summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2006-04-24 22:45:19 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2006-04-24 22:45:19 +0000
commit028e1ebc4a392572cae586d0e9044a32b867cba4 (patch)
tree36d3a748486feb3f41575708bef8b153fef2cad4
parent484debb45c5ea45bccf0f9cb05b1239a9c2244a3 (diff)
a) completely refactor abstract UI code
b) single-thread Tranzport implementation c) implement BasicUI to share functionality across multiple controllers d) various minor fixes here and there git-svn-id: svn://localhost/trunk/ardour2@468 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--SConstruct31
-rw-r--r--gtk2_ardour/SConscript1
-rw-r--r--gtk2_ardour/ardour_ui_ed.cc20
-rw-r--r--gtk2_ardour/editor.cc8
-rw-r--r--gtk2_ardour/editor_mixer.cc2
-rw-r--r--gtk2_ardour/gain_meter.cc6
-rw-r--r--gtk2_ardour/gui_thread.h2
-rw-r--r--gtk2_ardour/streamview.cc2
-rw-r--r--libs/ardour/SConscript2
-rw-r--r--libs/ardour/ardour/ardour.h3
-rw-r--r--libs/ardour/ardour/basic_ui.h34
-rw-r--r--libs/ardour/ardour/configuration.h3
-rw-r--r--libs/ardour/ardour/control_protocol.h48
-rw-r--r--libs/ardour/ardour/control_protocol_manager.h11
-rw-r--r--libs/ardour/ardour/session.h2
-rw-r--r--libs/ardour/basic_ui.cc189
-rw-r--r--libs/ardour/configuration.cc7
-rw-r--r--libs/ardour/control_protocol.cc214
-rw-r--r--libs/ardour/control_protocol_manager.cc69
-rw-r--r--libs/ardour/globals.cc18
-rw-r--r--libs/ardour/session_state.cc13
-rw-r--r--libs/ardour/session_transport.cc10
-rw-r--r--libs/gtkmm2ext/gtk_ui.cc380
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/gtk_ui.h159
-rw-r--r--libs/pbd3/SConscript19
-rw-r--r--libs/pbd3/base_ui.cc87
-rw-r--r--libs/pbd3/pbd/abstract_ui.h69
-rw-r--r--libs/pbd3/pbd/base_ui.h46
-rw-r--r--libs/pbd3/pbd/receiver.h2
-rw-r--r--libs/pbd3/pbd/ringbufferNPT.h2
-rw-r--r--libs/surfaces/generic_midi/SConscript2
-rw-r--r--libs/surfaces/generic_midi/generic_midi_control_protocol.cc10
-rw-r--r--libs/surfaces/generic_midi/generic_midi_control_protocol.h4
-rw-r--r--libs/surfaces/generic_midi/interface.cc2
-rw-r--r--libs/surfaces/tranzport/SConscript2
-rw-r--r--libs/surfaces/tranzport/interface.cc2
-rw-r--r--libs/surfaces/tranzport/tranzport_control_protocol.cc730
-rw-r--r--libs/surfaces/tranzport/tranzport_control_protocol.h77
38 files changed, 1211 insertions, 1077 deletions
diff --git a/SConstruct b/SConstruct
index 04d1b3545f..2c9749ea3c 100644
--- a/SConstruct
+++ b/SConstruct
@@ -35,7 +35,8 @@ opts.AddOptions(
BoolOption('VERSIONED', 'Add version information to ardour/gtk executable name inside the build directory', 0),
EnumOption('DIST_TARGET', 'Build target for cross compiling packagers', 'auto', allowed_values=('auto', 'i386', 'i686', 'x86_64', 'powerpc', 'tiger', 'panther', 'none' ), ignorecase=2),
BoolOption('FPU_OPTIMIZATION', 'Build runtime checked assembler code', 1),
- BoolOption('FFT_ANALYSIS', 'Include FFT analysis window', 0)
+ BoolOption('FFT_ANALYSIS', 'Include FFT analysis window', 0),
+ BoolOption('SURFACES', 'Build support for control surfaces', 0)
)
#----------------------------------------------------------------------
@@ -389,8 +390,6 @@ libraries['libgnomecanvas2'].ParseConfig ('pkg-config --cflags --libs libgnomeca
libraries['glade2'] = LibraryInfo()
libraries['glade2'].ParseConfig ('pkg-config --cflags --libs libglade-2.0')
-libraries['usb'] = LibraryInfo (LIBS='usb')
-
#libraries['flowcanvas'] = LibraryInfo(LIBS='flowcanvas', LIBPATH='#/libs/flowcanvas', CPPPATH='#libs/flowcanvas')
libraries['ardour'] = LibraryInfo (LIBS='ardour', LIBPATH='#libs/ardour', CPPPATH='#libs/ardour')
@@ -404,6 +403,21 @@ if env['VST']:
libraries['fst'].ParseConfig('pkg-config --cflags --libs libfst')
#
+# Check for libusb
+
+libraries['usb'] = LibraryInfo ()
+
+conf = Configure (libraries['usb'])
+if conf.CheckLib ('usb', 'usb_interrupt_write'):
+ have_libusb = 1
+else:
+ have_libusb = 0
+
+libraries['usb'] = conf.Finish ()
+
+#
+
+#
# Audio/MIDI library (needed for MIDI, since audio is all handled via JACK)
#
@@ -518,10 +532,13 @@ else:
'gtk2_ardour'
]
-surface_subdirs = [
- 'libs/surfaces/tranzport',
- 'libs/surfaces/generic_midi'
- ]
+surface_subdirs = []
+
+if env['SURFACES']:
+ surface_subdirs += [ 'libs/surfaces/generic_midi' ]
+ if have_libusb:
+ surface_subdirs += [ 'libs/surfaces/tranzport' ]
+
opts.Save('scache.conf', env)
Help(opts.GenerateHelpText(env))
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript
index 38cac52b40..74ba860efe 100644
--- a/gtk2_ardour/SConscript
+++ b/gtk2_ardour/SConscript
@@ -213,7 +213,6 @@ if env['FFT_ANALYSIS']:
intl_files += extra_sources
gtkardour.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
-gtkardour.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"")
gtkardour.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"")
gtkardour.Append(CXXFLAGS="-DGLADEPATH=\\\""+final_prefix+"/share/ardour/glade\\\"")
diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc
index 2a6bccf227..3944d3180a 100644
--- a/gtk2_ardour/ardour_ui_ed.cc
+++ b/gtk2_ardour/ardour_ui_ed.cc
@@ -446,6 +446,14 @@ ARDOUR_UI::install_actions ()
void
ARDOUR_UI::toggle_control_protocol (ControlProtocolInfo* cpi)
{
+ if (!session) {
+ /* this happens when we build the menu bar when control protocol support
+ has been used in the past for some given protocol - the item needs
+ to be made active, but there is no session yet.
+ */
+ return;
+ }
+
if (cpi->protocol == 0) {
ControlProtocolManager::instance().instantiate (*cpi);
} else {
@@ -465,13 +473,19 @@ ARDOUR_UI::build_control_surface_menu ()
for (i = ControlProtocolManager::instance().control_protocol_info.begin(); i != ControlProtocolManager::instance().control_protocol_info.end(); ++i) {
string action_name = "Toggle";
- action_name += (*i)->name;
+ action_name += legalize_for_path ((*i)->name);
action_name += "Surface";
string action_label = (*i)->name;
+
+ Glib::RefPtr<Action> act = ActionManager::register_toggle_action (editor->editor_actions, action_name.c_str(), action_label.c_str(),
+ (bind (mem_fun (*this, &ARDOUR_UI::toggle_control_protocol), *i)));
- ActionManager::register_toggle_action (editor->editor_actions, action_name.c_str(), action_label.c_str(),
- (bind (mem_fun (*this, &ARDOUR_UI::toggle_control_protocol), *i)));
+ Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
+
+ if ((*i)->protocol || (*i)->requested) {
+ tact->set_active ();
+ }
ui += "<menuitem action='";
ui += action_name;
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc
index 1ad81e18ef..1a44041f73 100644
--- a/gtk2_ardour/editor.cc
+++ b/gtk2_ardour/editor.cc
@@ -2283,18 +2283,10 @@ Editor::set_state (const XMLNode& node)
Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
bool yn = (prop->value() == X_("yes"));
- cerr << "at load time, show-editor-mixer = " << prop->value() << endl;
-
/* do it twice to force the change */
tact->set_active (!yn);
-
- cerr << "now reset to " << yn << endl;
-
tact->set_active (yn);
-
- cerr << "should be done\n";
-
}
}
diff --git a/gtk2_ardour/editor_mixer.cc b/gtk2_ardour/editor_mixer.cc
index 410474114a..f6fe3b66c9 100644
--- a/gtk2_ardour/editor_mixer.cc
+++ b/gtk2_ardour/editor_mixer.cc
@@ -36,8 +36,6 @@ Editor::editor_mixer_button_toggled ()
Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
if (act) {
Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic(act);
- bool yn = tact->get_active();
- cerr << "button toggled, state = " << yn << endl;
show_editor_mixer (tact->get_active());
}
}
diff --git a/gtk2_ardour/gain_meter.cc b/gtk2_ardour/gain_meter.cc
index 237e0787ea..159454ab18 100644
--- a/gtk2_ardour/gain_meter.cc
+++ b/gtk2_ardour/gain_meter.cc
@@ -152,13 +152,13 @@ GainMeter::GainMeter (IO& io, Session& s)
used to describe meter points. In english, its "input".
*/
- set_size_request_to_display_given_text (meter_point_button, _("tupni"), 2, 2);
+ set_size_request_to_display_given_text (meter_point_button, _("tupni"), 5, 5);
top_table.attach (meter_point_button, 1, 2, 0, 1);
}
gain_display_box.set_spacing (2);
- set_size_request_to_display_given_text (gain_display_frame, "-86.0", 2, 2);
+ set_size_request_to_display_given_text (gain_display_frame, "-86.g", 2, 6); /* note the descender */
gain_display_frame.set_shadow_type (Gtk::SHADOW_IN);
gain_display_frame.set_name ("BaseFrame");
gain_display_frame.add (gain_display);
@@ -166,7 +166,7 @@ GainMeter::GainMeter (IO& io, Session& s)
peak_display.set_name ("MixerStripPeakDisplay");
peak_display.add (peak_display_label);
- set_size_request_to_display_given_text (peak_display_frame, "-86.0", 2, 2);
+ set_size_request_to_display_given_text (peak_display_frame, "-86.g", 2, 6); /* note the descender */
peak_display_frame.set_shadow_type (Gtk::SHADOW_IN);
peak_display_frame.set_name ("BaseFrame");
peak_display_frame.add (peak_display);
diff --git a/gtk2_ardour/gui_thread.h b/gtk2_ardour/gui_thread.h
index a62a098654..22381e3536 100644
--- a/gtk2_ardour/gui_thread.h
+++ b/gtk2_ardour/gui_thread.h
@@ -4,7 +4,7 @@
#include <gtkmm2ext/gtk_ui.h>
#define ENSURE_GUI_THREAD(slot) \
- if (!Gtkmm2ext::UI::instance()->caller_is_gui_thread()) {\
+ if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) {\
Gtkmm2ext::UI::instance()->call_slot ((slot));\
return;\
}
diff --git a/gtk2_ardour/streamview.cc b/gtk2_ardour/streamview.cc
index 84e135d77c..bceaf09f99 100644
--- a/gtk2_ardour/streamview.cc
+++ b/gtk2_ardour/streamview.cc
@@ -275,7 +275,7 @@ StreamView::remove_rec_region (Region *r)
{
ENSURE_GUI_THREAD(bind (mem_fun (*this, &StreamView::remove_rec_region), r));
- if (!Gtkmm2ext::UI::instance()->caller_is_gui_thread()) {
+ if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) {
fatal << "region deleted from non-GUI thread!" << endmsg;
/*NOTREACHED*/
}
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
index 572851d2f2..3d83b3e276 100644
--- a/libs/ardour/SConscript
+++ b/libs/ardour/SConscript
@@ -29,6 +29,7 @@ audioregion.cc
auditioner.cc
automation.cc
automation_event.cc
+basic_ui.cc
configuration.cc
connection.cc
control_protocol.cc
@@ -99,6 +100,7 @@ if ardour['VST']:
ardour.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
ardour.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"")
+ardour.Append(CXXFLAGS="-DMODULE_DIR=\\\""+final_prefix+"/lib\\\"")
ardour.Append(CXXFLAGS="-DCONFIG_DIR=\\\""+final_config_prefix+"\\\"")
ardour.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"")
diff --git a/libs/ardour/ardour/ardour.h b/libs/ardour/ardour/ardour.h
index 45b45ecd06..28c5f07fce 100644
--- a/libs/ardour/ardour/ardour.h
+++ b/libs/ardour/ardour/ardour.h
@@ -50,7 +50,8 @@ namespace ARDOUR {
std::string get_user_ardour_path ();
- std::string get_system_ardour_path ();
+ std::string get_system_data_path ();
+ std::string get_system_module_path ();
std::string find_config_file (std::string name);
std::string find_data_file (std::string name, std::string subdir = "" );
diff --git a/libs/ardour/ardour/basic_ui.h b/libs/ardour/ardour/basic_ui.h
new file mode 100644
index 0000000000..aba8090add
--- /dev/null
+++ b/libs/ardour/ardour/basic_ui.h
@@ -0,0 +1,34 @@
+#ifndef __ardour_basic_ui_h__
+#define __ardour_basic_ui_h__
+
+namespace ARDOUR {
+ class Session;
+}
+
+class BasicUI {
+ public:
+ BasicUI (ARDOUR::Session&);
+ virtual ~BasicUI ();
+
+ void loop_toggle ();
+ void goto_start ();
+ void goto_end ();
+ void add_marker ();
+ void rewind ();
+ void ffwd ();
+ void transport_stop ();
+ void transport_play ();
+ void rec_enable_toggle ();
+ void save_state ();
+ void prev_marker ();
+ void next_marker ();
+ void move_at (float speed);
+ void undo ();
+ void redo ();
+ void toggle_all_rec_enables ();
+
+ protected:
+ ARDOUR::Session& session;
+};
+
+#endif /* __ardour_basic_ui_h__ */
diff --git a/libs/ardour/ardour/configuration.h b/libs/ardour/ardour/configuration.h
index d067bc6e61..60b5e8a2c3 100644
--- a/libs/ardour/ardour/configuration.h
+++ b/libs/ardour/ardour/configuration.h
@@ -60,6 +60,8 @@ class Configuration : public Stateful
int set_state (const XMLNode&);
XMLNode& get_state (void);
+ XMLNode* control_protocol_state () { return _control_protocol_state; }
+
/* define accessor methods */
#undef CONFIG_VARIABLE
@@ -88,6 +90,7 @@ class Configuration : public Stateful
#undef CONFIG_VARIABLE_SPECIAL
bool user_configuration;
+ XMLNode* _control_protocol_state;
XMLNode& state (bool user_only);
};
diff --git a/libs/ardour/ardour/control_protocol.h b/libs/ardour/ardour/control_protocol.h
index 64658fc199..70c7d2dc0d 100644
--- a/libs/ardour/ardour/control_protocol.h
+++ b/libs/ardour/ardour/control_protocol.h
@@ -5,37 +5,25 @@
#include <list>
#include <sigc++/sigc++.h>
+#include <ardour/basic_ui.h>
+
namespace ARDOUR {
class Route;
class Session;
-class ControlProtocol : public sigc::trackable {
+class ControlProtocol : public sigc::trackable, public BasicUI {
public:
ControlProtocol (Session&, std::string name);
virtual ~ControlProtocol();
- virtual int init () { return 0; }
-
- sigc::signal<void> ActiveChanged;
-
- enum SendWhat {
- SendRoute,
- SendGlobal
- };
-
std::string name() const { return _name; }
- void set_send (SendWhat);
- void set_active (bool yn);
- bool get_active() const { return active_thread > 0; }
+ virtual int set_active (bool yn) = 0;
+ bool get_active() const { return _active; }
- bool send() const { return _send != 0; }
- bool send_route_feedback () const { return _send & SendRoute; }
- bool send_global_feedback () const { return _send & SendGlobal; }
+ sigc::signal<void> ActiveChanged;
- virtual void send_route_feedback (std::list<Route*>&) {}
- virtual void send_global_feedback () {}
/* signals that a control protocol can emit and other (presumably graphical)
user interfaces can respond to
@@ -48,30 +36,8 @@ class ControlProtocol : public sigc::trackable {
static sigc::signal<void,float> ScrollTimeline;
protected:
-
- ARDOUR::Session& session;
- SendWhat _send;
std::string _name;
- int active_thread;
- int thread_request_pipe[2];
- pthread_t _thread;
-
- static void* _thread_work (void *);
- void* thread_work ();
-
- struct ThreadRequest {
- enum Type {
- Start,
- Stop,
- Quit
- };
- };
-
- int init_thread();
- int start_thread ();
- int stop_thread ();
- void terminate_thread ();
- int poke_thread (ThreadRequest::Type);
+ bool _active;
};
extern "C" {
diff --git a/libs/ardour/ardour/control_protocol_manager.h b/libs/ardour/ardour/control_protocol_manager.h
index b06c3024b6..c19649e38b 100644
--- a/libs/ardour/ardour/control_protocol_manager.h
+++ b/libs/ardour/ardour/control_protocol_manager.h
@@ -8,6 +8,8 @@
#include <pbd/lockmonitor.h>
+#include <ardour/stateful.h>
+
namespace ARDOUR {
class ControlProtocol;
@@ -19,9 +21,10 @@ struct ControlProtocolInfo {
ControlProtocol* protocol;
std::string name;
std::string path;
+ bool requested;
};
- class ControlProtocolManager : public sigc::trackable
+ class ControlProtocolManager : public sigc::trackable, public Stateful
{
public:
ControlProtocolManager ();
@@ -38,6 +41,11 @@ struct ControlProtocolInfo {
std::list<ControlProtocolInfo*> control_protocol_info;
+ static const std::string state_node_name;
+
+ int set_state (const XMLNode&);
+ XMLNode& get_state (void);
+
private:
static ControlProtocolManager* _instance;
@@ -49,6 +57,7 @@ struct ControlProtocolInfo {
int control_protocol_discover (std::string path);
ControlProtocolDescriptor* get_descriptor (std::string path);
+ ControlProtocolInfo* cpi_by_name (std::string);
};
} // namespace
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index 924a482936..57f01bf690 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -245,7 +245,7 @@ class Session : public sigc::trackable, public Stateful
std::string dead_sound_dir () const;
std::string automation_dir () const;
- static string suffixed_search_path (std::string suffix);
+ static string suffixed_search_path (std::string suffix, bool data);
static string control_protocol_path ();
static string template_path ();
static string template_dir ();
diff --git a/libs/ardour/basic_ui.cc b/libs/ardour/basic_ui.cc
new file mode 100644
index 0000000000..fcbd672cf4
--- /dev/null
+++ b/libs/ardour/basic_ui.cc
@@ -0,0 +1,189 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program 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.
+
+ This program 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 more 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <ardour/basic_ui.h>
+#include <ardour/session.h>
+#include <ardour/location.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+
+BasicUI::BasicUI (Session& s)
+ : session (s)
+{
+}
+
+BasicUI::~BasicUI ()
+{
+
+}
+
+void
+BasicUI::loop_toggle ()
+{
+ if (session.get_auto_loop()) {
+ session.request_auto_loop (false);
+ } else {
+ session.request_auto_loop (true);
+ if (!session.transport_rolling()) {
+ session.request_transport_speed (1.0);
+ }
+ }
+}
+
+void
+BasicUI::goto_start ()
+{
+ session.goto_start ();
+}
+
+void
+BasicUI::goto_end ()
+{
+ session.goto_end ();
+}
+
+void
+BasicUI::add_marker ()
+{
+ jack_nframes_t when = session.audible_frame();
+ session.locations()->add (new Location (when, when, _("unnamed"), Location::IsMark));
+}
+
+void
+BasicUI::rewind ()
+{
+ session.request_transport_speed (-2.0f);
+}
+
+void
+BasicUI::ffwd ()
+{
+ session.request_transport_speed (2.0f);
+}
+
+void
+BasicUI::transport_stop ()
+{
+ session.request_transport_speed (0.0);
+}
+
+void
+BasicUI::transport_play ()
+{
+ bool rolling = session.transport_rolling ();
+
+ if (session.get_auto_loop()) {
+ session.request_auto_loop (false);
+ }
+
+ if (session.get_play_range ()) {
+ session.request_play_range (false);
+ }
+
+ if (rolling) {
+ session.request_locate (session.last_transport_start(), true);
+
+ }
+
+ session.request_transport_speed (1.0f);
+}
+
+void
+BasicUI::rec_enable_toggle ()
+{
+ switch (session.record_status()) {
+ case Session::Disabled:
+ if (session.ntracks() == 0) {
+ // string txt = _("Please create 1 or more track\nbefore trying to record.\nCheck the Session menu.");
+ // MessageDialog msg (*editor, txt);
+ // msg.run ();
+ return;
+ }
+ session.maybe_enable_record ();
+ break;
+ case Session::Recording:
+ case Session::Enabled:
+ session.disable_record (true);
+ }
+}
+
+void
+BasicUI::save_state ()
+{
+ session.save_state ("");
+}
+
+void
+BasicUI::prev_marker ()
+{
+ Location *location = session.locations()->first_location_before (session.transport_frame());
+
+ if (location) {
+ session.request_locate (location->start(), session.transport_rolling());
+ } else {
+ session.goto_start ();
+ }
+}
+
+void
+BasicUI::next_marker ()
+{
+ Location *location = session.locations()->first_location_after (session.transport_frame());
+
+ if (location) {
+ session.request_locate (location->start(), session.transport_rolling());
+ } else {
+ session.request_locate (session.current_end_frame());
+ }
+}
+
+void
+BasicUI::move_at (float speed)
+{
+ session.request_transport_speed (speed);
+}
+
+void
+BasicUI::undo ()
+{
+ session.undo (1);
+}
+
+void
+BasicUI::redo ()
+{
+ session.redo (1);
+}
+
+void
+BasicUI::toggle_all_rec_enables ()
+{
+ if (session.get_record_enabled()) {
+ session.record_disenable_all ();
+ } else {
+ session.record_enable_all ();
+ }
+}
+
+
+
+
diff --git a/libs/ardour/configuration.cc b/libs/ardour/configuration.cc
index 1581e5216e..b7b1d65815 100644
--- a/libs/ardour/configuration.cc
+++ b/libs/ardour/configuration.cc
@@ -28,6 +28,7 @@
#include <ardour/configuration.h>
#include <ardour/diskstream.h>
#include <ardour/destructive_filesource.h>
+#include <ardour/control_protocol_manager.h>
#include "i18n.h"
@@ -55,6 +56,7 @@ Configuration::Configuration ()
user_configuration (false)
{
+ _control_protocol_state = 0;
}
Configuration::~Configuration ()
@@ -174,6 +176,8 @@ Configuration::state (bool user_only)
root->add_child_copy (*_extra_xml);
}
+ root->add_child_nocopy (ControlProtocolManager::instance().get_state());
+
return *root;
}
@@ -221,6 +225,9 @@ Configuration::set_state (const XMLNode& root)
} else if (node->name() == "extra") {
_extra_xml = new XMLNode (*node);
+
+ } else if (node->name() == ControlProtocolManager::state_node_name) {
+ _control_protocol_state = new XMLNode (*node);
}
}
diff --git a/libs/ardour/control_protocol.cc b/libs/ardour/control_protocol.cc
index 1e4bd8efc9..b7004a0ffa 100644
--- a/libs/ardour/control_protocol.cc
+++ b/libs/ardour/control_protocol.cc
@@ -18,22 +18,11 @@
$Id$
*/
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <poll.h>
-
-#include <pbd/pthread_utils.h>
-#include <pbd/error.h>
#include <ardour/control_protocol.h>
-#include <ardour/configuration.h>
-#include <ardour/session.h>
using namespace ARDOUR;
using namespace std;
-#include "i18n.h"
-
sigc::signal<void> ControlProtocol::ZoomToSession;
sigc::signal<void> ControlProtocol::ZoomOut;
sigc::signal<void> ControlProtocol::ZoomIn;
@@ -41,212 +30,13 @@ sigc::signal<void> ControlProtocol::Enter;
sigc::signal<void,float> ControlProtocol::ScrollTimeline;
ControlProtocol::ControlProtocol (Session& s, string str)
- : session (s),
+ : BasicUI (s),
_name (str)
{
- active_thread = 1;
- thread_request_pipe[0] = -1;
- thread_request_pipe[1] = -1;
+ _active = false;
}
ControlProtocol::~ControlProtocol ()
{
- terminate_thread ();
-
- if (thread_request_pipe[0] >= 0) {
- close (thread_request_pipe[0]);
- close (thread_request_pipe[1]);
- }
-}
-
-void
-ControlProtocol::set_send (SendWhat sw)
-{
- _send = sw;
-}
-
-int
-ControlProtocol::init_thread ()
-{
- if (pipe (thread_request_pipe) != 0) {
- error << string_compose (_("%1: cannot create thread request pipe (%1)"), _name, strerror (errno))
- << endmsg;
- return -1;
- }
-
- if (fcntl (thread_request_pipe[0], F_SETFL, O_NONBLOCK)) {
- error << string_compose(_("%1: cannot set O_NONBLOCK on read pipe (%2)"), _name, strerror (errno)) << endmsg;
- return -1;
- }
-
- if (fcntl (thread_request_pipe[1], F_SETFL, O_NONBLOCK)) {
- error << string_compose(_("%1: cannot set O_NONBLOCK on signal write pipe (%2)"), _name, strerror (errno)) << endmsg;
- return -1;
- }
-
- if (pthread_create_and_store ("tranzport delivery", &_thread, 0, _thread_work, this)) {
- error << string_compose (_("%1: could not create thread"), _name) << endmsg;
- return -1;
- }
-
- return 0;
-}
-
-int
-ControlProtocol::poke_thread (ThreadRequest::Type why)
-{
- char c = (char) why;
- return !(write (thread_request_pipe[1], &c, 1) == 1);
}
-int
-ControlProtocol::start_thread ()
-{
- return poke_thread (ThreadRequest::Start);
-}
-
-int
-ControlProtocol::stop_thread ()
-{
- return poke_thread (ThreadRequest::Stop);
-}
-
-void
-ControlProtocol::set_active (bool yn)
-{
- if (yn != active_thread) {
-
- if (yn) {
- /* make sure the feedback thread is alive */
- start_thread ();
- } else {
- /* maybe put the feedback thread to sleep */
- stop_thread ();
- }
-
- ActiveChanged ();
- }
-}
-
-void
-ControlProtocol::terminate_thread ()
-{
- void* status;
- poke_thread (ThreadRequest::Quit);
- pthread_join (_thread, &status);
-}
-
-void*
-ControlProtocol::_thread_work (void* arg)
-{
- return static_cast<ControlProtocol*> (arg)->thread_work ();
-}
-
-void*
-ControlProtocol::thread_work ()
-{
- PBD::ThreadCreated (pthread_self(), _name);
-
- struct pollfd pfd[1];
- int timeout;
-
- struct sched_param rtparam;
- int err;
-
- cerr << _name << " receiver thread running\n";
-
- memset (&rtparam, 0, sizeof (rtparam));
- rtparam.sched_priority = 3; /* XXX should be relative to audio (JACK) thread */
-
- if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
- // do we care? not particularly.
- info << string_compose (_("%1: delivery thread not running with realtime scheduling (%2)"), _name, strerror (errno)) << endmsg;
- }
-
- pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
- pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
-
-
- if (active_thread) {
- timeout = 10; // max (5, (int) Config->get_feedback_interval_ms());
- } else {
- timeout = -1;
- }
-
- while (1) {
-
- pfd[0].fd = thread_request_pipe[0];
- pfd[0].events = POLLIN|POLLHUP|POLLERR;
-
- if (poll (pfd, 1, timeout) < 0) {
- if (errno == EINTR) {
- continue;
- }
- error << string_compose (_("Protocol \"%1\" thread: poll failed (%2)"), _name, strerror (errno))
- << endmsg;
- break;
- }
-
- if (pfd[0].revents & ~POLLIN) {
- error << string_compose (_("Error thread request pipe for protocol \"%1\""), _name) << endmsg;
- break;
- }
-
- if (pfd[0].revents & POLLIN) {
-
- char req;
-
- /* empty the pipe of all current requests */
-
- while (1) {
- size_t nread = read (thread_request_pipe[0], &req, sizeof (req));
-
- if (nread == 1) {
- switch ((ThreadRequest::Type) req) {
-
- case ThreadRequest::Start:
- timeout = 10; // max (5, (int) Config->get_feedback_interval_ms());
- active_thread++;
- break;
-
- case ThreadRequest::Stop:
- timeout = -1;
- if (active_thread) {
- active_thread--;
- }
- break;
-
- case ThreadRequest::Quit:
- pthread_exit_pbd (0);
- /*NOTREACHED*/
- break;
-
- default:
- break;
- }
-
- } else if (nread == 0) {
- break;
- } else if (errno == EAGAIN) {
- break;
- } else {
- fatal << string_compose (_("Error reading from thread request pipe for protocol \"%1\""), _name) << endmsg;
- /*NOTREACHED*/
- }
- }
- }
-
- if (!active_thread) {
- continue;
- }
-
- if (send_route_feedback ()) {
- list<Route*> routes = session.get_routes(); /* copies the routes */
- send_route_feedback (routes);
- }
-
- send_global_feedback ();
- }
-
- return 0;
-}
diff --git a/libs/ardour/control_protocol_manager.cc b/libs/ardour/control_protocol_manager.cc
index 893124f0f5..ed33d0b6ee 100644
--- a/libs/ardour/control_protocol_manager.cc
+++ b/libs/ardour/control_protocol_manager.cc
@@ -15,6 +15,7 @@ using namespace std;
#include "i18n.h"
ControlProtocolManager* ControlProtocolManager::_instance = 0;
+const string ControlProtocolManager::state_node_name = X_("ControlProtocols");
ControlProtocolManager::ControlProtocolManager ()
{
@@ -42,6 +43,13 @@ ControlProtocolManager::set_session (Session& s)
{
_session = &s;
_session->going_away.connect (mem_fun (*this, &ControlProtocolManager::drop_session));
+
+ for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
+ if ((*i)->requested) {
+ instantiate (**i);
+ (*i)->requested = false;
+ }
+ }
}
void
@@ -122,6 +130,8 @@ ControlProtocolManager::discover_control_protocols (string path)
vector<string *> *found;
PathScanner scanner;
+ cerr << "looking for control protocols in " << path << endl;
+
found = scanner (path, protocol_filter, 0, false, true);
for (vector<string*>::iterator i = found->begin(); i != found->end(); ++i) {
@@ -145,9 +155,12 @@ ControlProtocolManager::control_protocol_discover (string path)
info->name = descriptor->name;
info->path = path;
info->protocol = 0;
+ info->requested = false;
control_protocol_info.push_back (info);
+ cerr << "discovered control surface protocol \"" << info->name << '"' << endl;
+
dlclose (descriptor->module);
}
@@ -195,3 +208,59 @@ ControlProtocolManager::foreach_known_protocol (sigc::slot<void,const ControlPro
method (*i);
}
}
+
+ControlProtocolInfo*
+ControlProtocolManager::cpi_by_name (string name)
+{
+ for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
+ if (name == (*i)->name) {
+ return *i;
+ }
+ }
+ return 0;
+}
+
+int
+ControlProtocolManager::set_state (const XMLNode& node)
+{
+ XMLNodeList clist;
+ XMLNodeConstIterator citer;
+ XMLProperty* prop;
+
+ clist = node.children();
+
+ for (citer = clist.begin(); citer != clist.end(); ++citer) {
+ if ((*citer)->name() == X_("Protocol")) {
+ if ((prop = (*citer)->property (X_("active"))) != 0) {
+ if (prop->value() == X_("yes")) {
+ if ((prop = (*citer)->property (X_("name"))) != 0) {
+ ControlProtocolInfo* cpi = cpi_by_name (prop->value());
+ if (cpi) {
+ if (_session) {
+ instantiate (*cpi);
+ } else {
+ cpi->requested = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+XMLNode&
+ControlProtocolManager::get_state (void)
+{
+ XMLNode* root = new XMLNode (state_node_name);
+ LockMonitor lm (protocols_lock, __LINE__, __FILE__);
+
+ for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
+ XMLNode* child = new XMLNode (X_("Protocol"));
+ child->add_property (X_("name"), (*i)->name);
+ child->add_property (X_("active"), (*i)->protocol ? "yes" : "no");
+ root->add_child_nocopy (*child);
+ }
+
+ return *root;
+}
diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc
index 5fca7f07eb..a6f9d6b2e5 100644
--- a/libs/ardour/globals.cc
+++ b/libs/ardour/globals.cc
@@ -269,6 +269,11 @@ ARDOUR::init (AudioEngine& engine, bool use_vst, bool try_optimization, void (*s
/* singleton - first object is "it" */
new ControlProtocolManager ();
ControlProtocolManager::instance().discover_control_protocols (Session::control_protocol_path());
+
+ XMLNode* node;
+ if ((node = Config->control_protocol_state()) != 0) {
+ ControlProtocolManager::instance().set_state (*node);
+ }
BoundsChanged = Change (StartChanged|PositionChanged|LengthChanged);
@@ -325,7 +330,7 @@ ARDOUR::get_user_ardour_path ()
}
string
-ARDOUR::get_system_ardour_path ()
+ARDOUR::get_system_data_path ()
{
string path;
@@ -335,6 +340,17 @@ ARDOUR::get_system_ardour_path ()
return path;
}
+string
+ARDOUR::get_system_module_path ()
+{
+ string path;
+
+ path += MODULE_DIR;
+ path += "/ardour2/";
+
+ return path;
+}
+
static string
find_file (string name, string dir, string subdir = "")
{
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 6f1d20d17f..0cab0fd28d 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -2293,7 +2293,7 @@ Session::template_dir ()
}
string
-Session::suffixed_search_path (string suffix)
+Session::suffixed_search_path (string suffix, bool data)
{
string path;
@@ -2301,7 +2301,12 @@ Session::suffixed_search_path (string suffix)
if (path[path.length()-1] != ':') {
path += ':';
}
- path += get_system_ardour_path();
+
+ if (data) {
+ path += get_system_data_path();
+ } else {
+ path += get_system_module_path();
+ }
vector<string> split_path;
@@ -2324,13 +2329,13 @@ Session::suffixed_search_path (string suffix)
string
Session::template_path ()
{
- return suffixed_search_path (X_("templates"));
+ return suffixed_search_path (X_("templates"), true);
}
string
Session::control_protocol_path ()
{
- return suffixed_search_path (X_("surfaces"));
+ return suffixed_search_path (X_("surfaces"), false);
}
int
diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc
index bf8b236600..6e756b28b7 100644
--- a/libs/ardour/session_transport.cc
+++ b/libs/ardour/session_transport.cc
@@ -946,17 +946,17 @@ Session::set_slave_source (SlaveSource src, jack_nframes_t frame)
bool reverse = false;
bool non_rt_required = false;
+ if (src == _slave_type) {
+ return 0;
+ }
+
if (_transport_speed) {
error << _("please stop the transport before adjusting slave settings") << endmsg;
/* help out non-MVC friendly UI's by telling them the slave type changed */
- ControlChanged (SlaveType); /* EMIT SIGNAL */
+ ControlChanged (SlaveType); /* EMIT SIGNAL */
return 0;
}
- if (src == _slave_type) {
- return 0;
- }
-
// if (src == JACK && Config->get_jack_time_master()) {
// return -1;
// }
diff --git a/libs/gtkmm2ext/gtk_ui.cc b/libs/gtkmm2ext/gtk_ui.cc
index 8dd5a18526..50d6114dee 100644
--- a/libs/gtkmm2ext/gtk_ui.cc
+++ b/libs/gtkmm2ext/gtk_ui.cc
@@ -48,21 +48,23 @@ using std::map;
pthread_t UI::gui_thread;
UI *UI::theGtkUI = 0;
-UI::UI (string name, int *argc, char ***argv, string rcfile)
- : _ui_name (name)
+BaseUI::RequestType Gtkmm2ext::ErrorMessage = BaseUI::new_request_type();
+BaseUI::RequestType Gtkmm2ext::Quit = BaseUI::new_request_type();
+BaseUI::RequestType Gtkmm2ext::TouchDisplay = BaseUI::new_request_type();
+BaseUI::RequestType Gtkmm2ext::StateChange = BaseUI::new_request_type();
+BaseUI::RequestType Gtkmm2ext::SetTip = BaseUI::new_request_type();
+BaseUI::RequestType Gtkmm2ext::AddIdle = BaseUI::new_request_type();
+BaseUI::RequestType Gtkmm2ext::AddTimeout = BaseUI::new_request_type();
+
+#include <pbd/abstract_ui.cc> /* instantiate the template */
+
+
+UI::UI (string namestr, int *argc, char ***argv, string rcfile)
+ : AbstractUI<UIRequest> (namestr, true)
{
theMain = new Main (argc, argv);
tips = new Tooltips;
- if (pthread_key_create (&thread_request_buffer_key, 0)) {
- cerr << _("cannot create thread request buffer key") << endl;
- throw failed_constructor();
- }
-
- PBD::ThreadCreated.connect (mem_fun (*this, &UI::register_thread));
- PBD::ThreadCreatedWithRequestSize.connect (mem_fun (*this, &UI::register_thread_with_request_count));
-
- _ok = false;
_active = false;
if (!theGtkUI) {
@@ -73,33 +75,39 @@ UI::UI (string name, int *argc, char ***argv, string rcfile)
/* NOTREACHED */
}
- if (setup_signal_pipe ()) {
- return;
- }
+ /* add the pipe to the select/poll loop that GDK does */
+
+ gdk_input_add (signal_pipe[0],
+ GDK_INPUT_READ,
+ UI::signal_pipe_callback,
+ this);
errors = new TextViewer (850,100);
errors->text().set_editable (false);
errors->text().set_name ("ErrorText");
string title;
- title = _ui_name;
+ title = namestr;
title += ": Log";
errors->set_title (title);
errors->dismiss_button().set_name ("ErrorLogCloseButton");
- errors->signal_delete_event().connect (bind (ptr_fun (just_hide_it), (Window *) errors));
+ errors->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors));
register_thread (pthread_self(), X_("GUI"));
load_rcfile (rcfile);
-
- _ok = true;
}
UI::~UI ()
{
- close (signal_pipe[0]);
- close (signal_pipe[1]);
+}
+
+
+bool
+UI::caller_is_ui_thread ()
+{
+ return pthread_equal (gui_thread, pthread_self());
}
int
@@ -226,7 +234,13 @@ UI::kill ()
void
UI::quit ()
{
- request (Quit);
+ UIRequest *req = get_request (Quit);
+
+ if (req == 0) {
+ return;
+ }
+
+ send_request (req);
}
static bool idle_quit ()
@@ -241,16 +255,10 @@ UI::do_quit ()
Glib::signal_idle().connect (sigc::ptr_fun (idle_quit));
}
-int
-UI::set_quit_context()
-{
- return setjmp (quit_context);
-}
-
void
UI::touch_display (Touchable *display)
{
- Request *req = get_request (TouchDisplay);
+ UIRequest *req = get_request (TouchDisplay);
if (req == 0) {
return;
@@ -262,51 +270,9 @@ UI::touch_display (Touchable *display)
}
void
-UI::call_slot (sigc::slot<void> slot)
-{
- Request *req = get_request (CallSlot);
-
- if (req == 0) {
- return;
- }
-
- req->slot = slot;
-
- send_request (req);
-}
-
-void
-UI::call_slot_locked (sigc::slot<void> slot)
-{
- if (caller_is_gui_thread()) {
- call_slot (slot);
- return;
- }
-
- Request *req = get_request (CallSlotLocked);
-
- if (req == 0) {
- return;
- }
-
- req->slot = slot;
-
- pthread_mutex_init (&req->slot_lock, NULL);
- pthread_cond_init (&req->slot_cond, NULL);
- pthread_mutex_lock (&req->slot_lock);
-
- send_request (req);
-
- pthread_cond_wait (&req->slot_cond, &req->slot_lock);
- pthread_mutex_unlock (&req->slot_lock);
-
- delete req;
-}
-
-void
UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp)
{
- Request *req = get_request (SetTip);
+ UIRequest *req = get_request (SetTip);
if (req == 0) {
return;
@@ -322,7 +288,7 @@ UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp)
void
UI::set_state (Widget *w, StateType state)
{
- Request *req = get_request (StateChange);
+ UIRequest *req = get_request (StateChange);
if (req == 0) {
return;
@@ -337,22 +303,7 @@ UI::set_state (Widget *w, StateType state)
void
UI::idle_add (int (*func)(void *), void *arg)
{
- Request *req = get_request (AddIdle);
-
- if (req == 0) {
- return;
- }
-
- req->function = func;
- req->arg = arg;
-
- send_request (req);
-}
-
-void
-UI::timeout_add (unsigned int timeout, int (*func)(void *), void *arg)
-{
- Request *req = get_request (AddTimeout);
+ UIRequest *req = get_request (AddIdle);
if (req == 0) {
return;
@@ -360,113 +311,12 @@ UI::timeout_add (unsigned int timeout, int (*func)(void *), void *arg)
req->function = func;
req->arg = arg;
- req->timeout = timeout;
send_request (req);
}
/* END abstract_ui interfaces */
-/* Handling requests */
-
-void
-UI::register_thread (pthread_t thread_id, string name)
-{
- register_thread_with_request_count (thread_id, name, 256);
-}
-
-void
-UI::register_thread_with_request_count (pthread_t thread_id, string name, uint32_t num_requests)
-{
- RingBufferNPT<Request>* b = new RingBufferNPT<Request> (num_requests);
-
- {
- PBD::LockMonitor lm (request_buffer_map_lock, __LINE__, __FILE__);
- request_buffers[thread_id] = b;
- }
-
- pthread_setspecific (thread_request_buffer_key, b);
-}
-
-UI::Request::Request()
-{
-
-}
-
-UI::Request*
-UI::get_request (RequestType rt)
-{
- RingBufferNPT<Request>* rbuf = static_cast<RingBufferNPT<Request>* >(pthread_getspecific (thread_request_buffer_key));
-
- if (rbuf == 0) {
- /* Cannot happen, but if it does we can't use the error reporting mechanism */
- cerr << _("programming error: ")
- << string_compose (X_("no GUI request buffer found for thread %1"), pthread_self())
- << endl;
- abort ();
- }
-
- RingBufferNPT<Request>::rw_vector vec;
-
- rbuf->get_write_vector (&vec);
-
- if (vec.len[0] == 0) {
- if (vec.len[1] == 0) {
- cerr << string_compose (X_("no space in GUI request buffer for thread %1"), pthread_self())
- << endl;
- return 0;
- } else {
- vec.buf[1]->type = rt;
- return vec.buf[1];
- }
- } else {
- vec.buf[0]->type = rt;
- return vec.buf[0];
- }
-}
-
-int
-UI::setup_signal_pipe ()
-{
- /* setup the pipe that other threads send us notifications/requests
- through.
- */
-
- if (pipe (signal_pipe)) {
- error << "UI: cannot create error signal pipe ("
- << std::strerror (errno) << ")"
- << endmsg;
-
- return -1;
- }
-
- if (fcntl (signal_pipe[0], F_SETFL, O_NONBLOCK)) {
- error << "UI: cannot set O_NONBLOCK on "
- "signal read pipe ("
- << std::strerror (errno) << ")"
- << endmsg;
- return -1;
- }
-
- if (fcntl (signal_pipe[1], F_SETFL, O_NONBLOCK)) {
- error << "UI: cannot set O_NONBLOCK on "
- "signal write pipe ("
- << std::strerror (errno)
- << ")"
- << endmsg;
- return -1;
- }
-
- /* add the pipe to the select/poll loop that GDK does */
-
- gdk_input_add (signal_pipe[0],
- GDK_INPUT_READ,
- UI::signal_pipe_callback,
- this);
-
- return 0;
-}
-
void
UI::signal_pipe_callback (void *arg, int fd, GdkInputCondition cond)
{
@@ -480,145 +330,43 @@ UI::signal_pipe_callback (void *arg, int fd, GdkInputCondition cond)
}
void
-UI::handle_ui_requests ()
+UI::do_request (UIRequest* req)
{
- RequestBufferMap::iterator i;
-
- request_buffer_map_lock.lock ();
-
- for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
-
- RingBufferNPT<Request>::rw_vector vec;
-
- while (true) {
-
- /* we must process requests 1 by 1 because
- the request may run a recursive main
- event loop that will itself call
- handle_ui_requests. when we return
- from the request handler, we cannot
- expect that the state of queued requests
- is even remotely consistent with
- the condition before we called it.
- */
-
- i->second->get_read_vector (&vec);
-
- if (vec.len[0] == 0) {
- break;
- } else {
- /* copy constructor does a deep
- copy of the Request object,
- unlike Ringbuffer::read()
- */
- Request req (*vec.buf[0]);
- i->second->increment_read_ptr (1);
- request_buffer_map_lock.unlock ();
- do_request (&req);
- request_buffer_map_lock.lock ();
- }
- }
- }
-
- request_buffer_map_lock.unlock ();
-}
+ if (req->type == ErrorMessage) {
-void
-UI::do_request (Request* req)
-{
- switch (req->type) {
- case ErrorMessage:
process_error_message (req->chn, req->msg);
free (const_cast<char*>(req->msg)); /* it was strdup'ed */
req->msg = 0; /* don't free it again in the destructor */
- break;
-
- case Quit:
+
+ } else if (req->type == Quit) {
+
do_quit ();
- break;
-
- case CallSlot:
- req->slot ();
- break;
- case CallSlotLocked:
- pthread_mutex_lock (&req->slot_lock);
+ } else if (req->type == CallSlot) {
+
req->slot ();
- pthread_cond_signal (&req->slot_cond);
- pthread_mutex_unlock (&req->slot_lock);
- break;
-
- case TouchDisplay:
+
+ } else if (req->type == TouchDisplay) {
+
req->display->touch ();
if (req->display->delete_after_touch()) {
delete req->display;
}
- break;
-
- case StateChange:
+
+ } else if (req->type == StateChange) {
+
req->widget->set_state (req->new_state);
- break;
-
- case SetTip:
- /* XXX need to figure out how this works */
- break;
-
- case AddIdle:
- gtk_idle_add (req->function, req->arg);
- break;
-
- case AddTimeout:
- gtk_timeout_add (req->timeout, req->function, req->arg);
- break;
-
- default:
- error << "UI: unknown request type "
- << (int) req->type
- << endmsg;
- }
-}
-void
-UI::send_request (Request *req)
-{
- if (instance() == 0) {
- return; /* XXX is this the right thing to do ? */
- }
-
- if (caller_is_gui_thread()) {
- // cerr << "GUI thread sent request " << req << " type = " << req->type << endl;
- do_request (req);
- } else {
- const char c = 0;
- RingBufferNPT<Request*>* rbuf = static_cast<RingBufferNPT<Request*> *> (pthread_getspecific (thread_request_buffer_key));
-
- if (rbuf == 0) {
- /* can't use the error system to report this, because this
- thread isn't registered!
- */
- cerr << _("programming error: ")
- << string_compose (X_("UI::send_request() called from %1, but no request buffer exists for that thread"),
- pthread_self())
- << endl;
- abort ();
- }
-
- // cerr << "thread " << pthread_self() << " sent request " << req << " type = " << req->type << endl;
- rbuf->increment_write_ptr (1);
- write (signal_pipe[1], &c, 1);
- }
-}
+ } else if (req->type == SetTip) {
-void
-UI::request (RequestType rt)
-{
- Request *req = get_request (rt);
+ /* XXX need to figure out how this works */
- if (req == 0) {
- return;
- }
+ } else {
- send_request (req);
+ error << "GtkUI: unknown request type "
+ << (int) req->type
+ << endmsg;
+ }
}
/*======================================================================
@@ -628,10 +376,10 @@ UI::request (RequestType rt)
void
UI::receive (Transmitter::Channel chn, const char *str)
{
- if (caller_is_gui_thread()) {
+ if (caller_is_ui_thread()) {
process_error_message (chn, str);
} else {
- Request* req = get_request (ErrorMessage);
+ UIRequest* req = get_request (ErrorMessage);
if (req == 0) {
return;
@@ -763,7 +511,7 @@ UI::handle_fatal (const char *message)
win.set_default_size (400, 100);
string title;
- title = _ui_name;
+ title = name();
title += ": Fatal Error";
win.set_title (title);
@@ -787,7 +535,7 @@ UI::popup_error (const char *text)
{
PopUp *pup;
- if (!caller_is_gui_thread()) {
+ if (!caller_is_ui_thread()) {
error << "non-UI threads can't use UI::popup_error"
<< endmsg;
return;
@@ -802,7 +550,7 @@ UI::popup_error (const char *text)
void
UI::flush_pending ()
{
- if (!caller_is_gui_thread()) {
+ if (!caller_is_ui_thread()) {
error << "non-UI threads cannot call UI::flush_pending()"
<< endmsg;
return;
diff --git a/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h b/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h
index b9f7c31e96..5446374d7b 100644
--- a/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h
+++ b/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h
@@ -39,6 +39,7 @@
#include <pbd/atomic.h>
#include <pbd/pool.h>
#include <pbd/error.h>
+#include <pbd/receiver.h>
#include <pbd/lockmonitor.h>
using std::string;
@@ -50,8 +51,42 @@ namespace Gtkmm2ext {
class TextViewer;
-class UI : public AbstractUI
+extern BaseUI::RequestType ErrorMessage;
+extern BaseUI::RequestType Quit;
+extern BaseUI::RequestType CallSlot;
+extern BaseUI::RequestType TouchDisplay;
+extern BaseUI::RequestType StateChange;
+extern BaseUI::RequestType SetTip;
+extern BaseUI::RequestType AddIdle;
+extern BaseUI::RequestType AddTimeout;
+
+struct UIRequest : public BaseUI::BaseRequestObject {
+
+ /* this once used anonymous unions to merge elements
+ that are never part of the same request. that makes
+ the creation of a legal copy constructor difficult
+ because of the semantics of the slot member.
+ */
+
+ Touchable *display;
+ const char *msg;
+ Gtk::StateType new_state;
+ int (*function)(void *);
+ Gtk::Widget *widget;
+ Transmitter::Channel chn;
+ void *arg;
+ const char *msg2;
+ sigc::slot<void> slot;
+
+ ~UIRequest () {
+ if (type == ErrorMessage && msg) {
+ /* msg was strdup()'ed */
+ free ((char *)msg);
+ }
+ }
+ };
+class UI : public Receiver, public AbstractUI<UIRequest>
{
public:
UI (string name, int *argc, char **argv[], string rcfile);
@@ -59,36 +94,29 @@ class UI : public AbstractUI
static UI *instance() { return theGtkUI; }
+ /* receiver interface */
+
+ void receive (Transmitter::Channel, const char *);
+
/* Abstract UI interfaces */
+ bool caller_is_ui_thread ();
+
+ /* Gtk-UI specific interfaces */
+
bool running ();
void quit ();
void kill ();
int load_rcfile (string);
- void request (RequestType);
void run (Receiver &old_receiver);
- void call_slot (sigc::slot<void>);
- void call_slot_locked (sigc::slot<void>);
- void touch_display (Touchable *);
- void receive (Transmitter::Channel, const char *);
-
- void register_thread (pthread_t, string);
- void register_thread_with_request_count (pthread_t, string, uint32_t num_requests);
-
- bool caller_is_gui_thread () {
- return pthread_equal (gui_thread, pthread_self());
- }
- /* Gtk-UI specific interfaces */
-
- int set_quit_context ();
- void set_tip (Gtk::Widget *, const gchar *txt, const gchar *hlp = 0);
void set_state (Gtk::Widget *w, Gtk::StateType state);
- void idle_add (int (*)(void *), void *);
- void timeout_add (unsigned int, int (*)(void *), void *);
void popup_error (const char *text);
void flush_pending ();
void toggle_errors ();
+ void touch_display (Touchable *);
+ void set_tip (Gtk::Widget *w, const gchar *tip, const gchar *hlp);
+ void idle_add (int (*func)(void *), void *arg);
template<class T> static bool idle_delete (T *obj) { delete obj; return false; }
template<class T> static void delete_when_idle (T *obj) {
@@ -106,6 +134,8 @@ class UI : public AbstractUI
static bool just_hide_it (GdkEventAny *, Gtk::Window *);
+ static pthread_t the_gui_thread() { return gui_thread; }
+
protected:
virtual void handle_fatal (const char *);
virtual void display_message (const char *prefix, gint prefix_len,
@@ -113,58 +143,10 @@ class UI : public AbstractUI
Glib::RefPtr<Gtk::TextBuffer::Tag> mtag,
const char *msg);
- /* stuff to invoke member functions in another
- thread so that we can keep the GUI running.
- */
-
- template<class UI_CLASS> struct thread_arg {
- UI_CLASS *ui;
- void (UI_CLASS::*func)(void *);
- void *arg;
- };
-
- template<class UI_CLASS> static void *start_other_thread (void *arg);
- template<class UI_CLASS> void other_thread (void (UI_CLASS::*func)(void *), void *arg = 0);
-
private:
- struct Request {
-
- /* this once used anonymous unions to merge elements
- that are never part of the same request. that makes
- the creation of a legal copy constructor difficult
- because of the semantics of the slot member.
- */
-
- RequestType type;
- Touchable *display;
- const char *msg;
- Gtk::StateType new_state;
- int (*function)(void *);
- Gtk::Widget *widget;
- Transmitter::Channel chn;
- void *arg;
- const char *msg2;
- unsigned int timeout;
- sigc::slot<void> slot;
-
- /* this is for CallSlotLocked requests */
-
- pthread_mutex_t slot_lock;
- pthread_cond_t slot_cond;
-
- Request ();
- ~Request () {
- if (type == ErrorMessage && msg) {
- /* msg was strdup()'ed */
- free ((char *)msg);
- }
- }
- };
-
static UI *theGtkUI;
static pthread_t gui_thread;
bool _active;
- string _ui_name;
Gtk::Main *theMain;
Gtk::Tooltips *tips;
TextViewer *errors;
@@ -177,18 +159,6 @@ class UI : public AbstractUI
Glib::RefPtr<Gtk::TextBuffer::Tag> warning_ptag;
Glib::RefPtr<Gtk::TextBuffer::Tag> warning_mtag;
- int signal_pipe[2];
- PBD::Lock request_buffer_map_lock;
- typedef std::map<pthread_t,RingBufferNPT<Request>* > RequestBufferMap;
- RequestBufferMap request_buffers;
- Request* get_request(RequestType);
- pthread_key_t thread_request_buffer_key;
-
- int setup_signal_pipe ();
-
- void handle_ui_requests ();
- void do_request (Request *);
- void send_request (Request *);
static void signal_pipe_callback (void *, gint, GdkInputCondition);
void process_error_message (Transmitter::Channel, const char *);
void do_quit ();
@@ -197,38 +167,9 @@ class UI : public AbstractUI
bool color_selection_deleted (GdkEventAny *);
bool color_picked;
- jmp_buf quit_context;
+ void do_request (UIRequest*);
};
-template<class UI_CLASS> void *
-UI::start_other_thread (void *arg)
-
-{
- thread_arg<UI_CLASS> *ta = (thread_arg<UI_CLASS> *) arg;
- (ta->ui->*ta->func)(ta->arg);
- delete ta;
- return 0;
-}
-
-template<class UI_CLASS> void
-UI::other_thread (void (UI_CLASS::*func)(void *), void *arg)
-
-{
- pthread_t thread_id;
- thread_arg<UI_CLASS> *ta = new thread_arg<UI_CLASS>;
-
- ta->ui = dynamic_cast<UI_CLASS *> (this);
- if (ta->ui == 0) {
- error << "UI::other thread called illegally"
- << endmsg;
- return;
- }
- ta->func = func;
- ta->arg = arg;
- pthread_create (&thread_id, 0, start_other_thread, ta);
-}
-
-
} /* namespace */
#endif /* __pbd_gtk_ui_h__ */
diff --git a/libs/pbd3/SConscript b/libs/pbd3/SConscript
index 46089248e7..eae438336d 100644
--- a/libs/pbd3/SConscript
+++ b/libs/pbd3/SConscript
@@ -2,15 +2,22 @@
import glob
-Import('env libraries')
+Import('env libraries i18n')
pbd3 = env.Copy()
domain = 'libpbd'
+
pbd3.Append(DOMAIN=domain,MAJOR=3,MINOR=2,MICRO=0)
+pbd3.Append(CXXFLAGS="-DPACKAGE=\\\"" + domain + "\\\"")
+pbd3.Append(CXXFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
+pbd3.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
+pbd3.Append(PACKAGE=domain)
+pbd3.Append(POTFILE=domain + '.pot')
pbd3_files = Split("""
basename.cc
+base_ui.cc
dirname.cc
dmalloc.cc
mountpoint.cc
@@ -38,8 +45,6 @@ pbd3 = conf.Finish()
pbd3.Merge ([ libraries['sigc2'], libraries['xml'] ])
-pbd3.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
-pbd3.Append(CCFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
pbd3.VersionBuild(['version.cc','pbd/version.h'], 'SConscript')
@@ -50,5 +55,11 @@ else:
Default(libpbd3)
+if env['NLS']:
+ i18n (pbd3, pbd3_files, env)
+
env.Alias('tarball', env.Distribute (env['DISTTREE'],
- [ 'SConscript' ] + pbd3_files + glob.glob('pbd/*.h')))
+ [ 'SConscript' ] +
+ pbd3_files +
+ glob.glob('po/*.po') +
+ glob.glob('pbd/*.h')))
diff --git a/libs/pbd3/base_ui.cc b/libs/pbd3/base_ui.cc
new file mode 100644
index 0000000000..5598b20021
--- /dev/null
+++ b/libs/pbd3/base_ui.cc
@@ -0,0 +1,87 @@
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <pbd/base_ui.h>
+#include <pbd/error.h>
+#include <pbd/compose.h>
+#include <pbd/failed_constructor.h>
+
+#include "i18n.h"
+
+using namespace std;
+
+uint32_t BaseUI::rt_bit = 1;
+BaseUI::RequestType BaseUI::CallSlot = BaseUI::new_request_type();
+
+BaseUI::BaseUI (string str, bool with_signal_pipe)
+ : _name (str)
+{
+ /* odd pseudo-singleton semantics */
+
+ base_ui_instance = this;
+
+ signal_pipe[0] = -1;
+ signal_pipe[1] = -1;
+
+ if (with_signal_pipe) {
+ if (setup_signal_pipe ()) {
+ throw failed_constructor ();
+ }
+ }
+}
+
+BaseUI::~BaseUI()
+{
+ if (signal_pipe[0] >= 0) {
+ close (signal_pipe[0]);
+ }
+
+ if (signal_pipe[1] >= 0) {
+ close (signal_pipe[1]);
+ }
+}
+
+BaseUI::RequestType
+BaseUI::new_request_type ()
+{
+ RequestType rt;
+
+ /* XXX catch out-of-range */
+
+ rt = RequestType (rt_bit);
+ rt_bit <<= 1;
+
+ return rt;
+}
+
+int
+BaseUI::setup_signal_pipe ()
+{
+ /* setup the pipe that other threads send us notifications/requests
+ through.
+ */
+
+ if (pipe (signal_pipe)) {
+ error << string_compose (_("%1-UI: cannot create error signal pipe (%2)"), _name, std::strerror (errno))
+ << endmsg;
+
+ return -1;
+ }
+
+ if (fcntl (signal_pipe[0], F_SETFL, O_NONBLOCK)) {
+ error << string_compose (_("%1-UI: cannot set O_NONBLOCK on signal read pipe (%2)"), _name, std::strerror (errno))
+ << endmsg;
+ return -1;
+ }
+
+ if (fcntl (signal_pipe[1], F_SETFL, O_NONBLOCK)) {
+ error << string_compose (_("%1-UI: cannot set O_NONBLOCK on signal write pipe (%2)"), _name, std::strerror (errno))
+ << endmsg;
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/libs/pbd3/pbd/abstract_ui.h b/libs/pbd3/pbd/abstract_ui.h
index 0c845c15cd..98f077cb48 100644
--- a/libs/pbd3/pbd/abstract_ui.h
+++ b/libs/pbd3/pbd/abstract_ui.h
@@ -21,47 +21,58 @@
#ifndef __pbd_abstract_ui_h__
#define __pbd_abstract_ui_h__
-#include <pbd/receiver.h>
+#include <map>
+#include <string>
+#include <pthread.h>
+
#include <sigc++/sigc++.h>
+#include <pbd/receiver.h>
+#include <pbd/lockmonitor.h>
+#include <pbd/ringbufferNPT.h>
+#include <pbd/base_ui.h>
+
class Touchable;
-class AbstractUI : public Receiver
+template <class RequestObject>
+class AbstractUI : public BaseUI
{
public:
- enum RequestType {
- ErrorMessage,
- Quit,
- CallSlot,
- CallSlotLocked,
- TouchDisplay,
- StateChange,
- SetTip,
- AddIdle,
- AddTimeout,
- };
-
- bool ok() { return _ok; }
-
- AbstractUI () {}
+ AbstractUI (std::string name, bool with_signal_pipe);
virtual ~AbstractUI() {}
- virtual void run (Receiver &old_receiver) = 0;
- virtual void quit () = 0;
- virtual bool running () = 0;
- virtual void request (RequestType) = 0;
- virtual void touch_display (Touchable *) = 0;
- virtual void call_slot (sigc::slot<void>) = 0;
- virtual bool caller_is_gui_thread() = 0;
+ virtual bool caller_is_ui_thread() = 0;
+
+ void call_slot (sigc::slot<void> el_slot) {
+ RequestObject *req = get_request (BaseUI::CallSlot);
+
+ if (req == 0) {
+ return;
+ }
+
+ req->slot = el_slot;
+ send_request (req);
+ }
- /* needed to be a receiver ... */
+ void register_thread (pthread_t, std::string);
+ void register_thread_with_request_count (pthread_t, std::string, uint32_t num_requests);
- virtual void receive (Transmitter::Channel, const char *) = 0;
-
protected:
- bool _ok;
+ typedef RingBufferNPT<RequestObject> RequestBuffer;
+ typedef typename RequestBuffer::rw_vector RequestBufferVector;
+ typedef typename std::map<pthread_t,RequestBuffer*>::iterator RequestBufferMapIterator;
+
+ PBD::Lock request_buffer_map_lock;
+ typedef std::map<pthread_t,RequestBuffer*> RequestBufferMap;
+ RequestBufferMap request_buffers;
+ pthread_key_t thread_request_buffer_key;
+ RequestObject* get_request (RequestType);
+ void handle_ui_requests ();
+ void send_request (RequestObject *);
+
+ virtual void do_request (RequestObject *) = 0;
};
-#endif // __pbd_abstract_ui_h__
+#endif /* __pbd_abstract_ui_h__ */
diff --git a/libs/pbd3/pbd/base_ui.h b/libs/pbd3/pbd/base_ui.h
new file mode 100644
index 0000000000..d9b546ff75
--- /dev/null
+++ b/libs/pbd3/pbd/base_ui.h
@@ -0,0 +1,46 @@
+#ifndef __pbd_base_ui_h__
+#define __pbd_base_ui_h__
+
+#include <string>
+#include <stdint.h>
+
+#include <sigc++/slot.h>
+#include <sigc++/trackable.h>
+
+class BaseUI : virtual public sigc::trackable {
+ public:
+ BaseUI (std::string name, bool with_signal_pipes);
+ virtual ~BaseUI();
+
+ BaseUI* base_instance() { return base_ui_instance; }
+
+ std::string name() const { return _name; }
+
+ bool ok() const { return _ok; }
+
+ enum RequestType {
+ range_guarantee = ~0
+ };
+
+ struct BaseRequestObject {
+ RequestType type;
+ sigc::slot<void> the_slot;
+ };
+
+ static RequestType new_request_type();
+ static RequestType BaseUI::CallSlot;
+
+ protected:
+ int signal_pipe[2];
+ bool _ok;
+
+ private:
+ std::string _name;
+ BaseUI* base_ui_instance;
+
+ static uint32_t rt_bit;
+
+ int setup_signal_pipe ();
+};
+
+#endif /* __pbd_base_ui_h__ */
diff --git a/libs/pbd3/pbd/receiver.h b/libs/pbd3/pbd/receiver.h
index b55e28f5ee..5ce238df63 100644
--- a/libs/pbd3/pbd/receiver.h
+++ b/libs/pbd3/pbd/receiver.h
@@ -31,7 +31,7 @@ using std::vector;
class strstream;
-class Receiver : public sigc::trackable
+class Receiver : virtual public sigc::trackable
{
public:
Receiver ();
diff --git a/libs/pbd3/pbd/ringbufferNPT.h b/libs/pbd3/pbd/ringbufferNPT.h
index d0562ec76b..d559a53d94 100644
--- a/libs/pbd3/pbd/ringbufferNPT.h
+++ b/libs/pbd3/pbd/ringbufferNPT.h
@@ -24,6 +24,8 @@
#include <sys/mman.h>
#include <pbd/atomic.h>
+/* ringbuffer class where the element size is not required to be a power of two */
+
template<class T>
class RingBufferNPT
{
diff --git a/libs/surfaces/generic_midi/SConscript b/libs/surfaces/generic_midi/SConscript
index 51e0ff88c8..7e6ef1cf85 100644
--- a/libs/surfaces/generic_midi/SConscript
+++ b/libs/surfaces/generic_midi/SConscript
@@ -46,6 +46,8 @@ Default(libardour_genericmidi)
if env['NLS']:
i18n (genericmidi, genericmidi_files, env)
+env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2/surfaces'), libardour_genericmidi))
+
env.Alias('tarball', env.Distribute (env['DISTTREE'],
[ 'SConscript', 'i18n.h', 'gettext.h' ] +
genericmidi_files +
diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc
index 61a8b7974e..86f8934c05 100644
--- a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc
+++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc
@@ -22,10 +22,10 @@ GenericMidiControlProtocol::~GenericMidiControlProtocol ()
}
int
-GenericMidiControlProtocol::init ()
+GenericMidiControlProtocol::set_active (bool yn)
{
/* start delivery/outbound thread */
- return init_thread ();
+ return 0;
}
void
@@ -63,9 +63,3 @@ GenericMidiControlProtocol::send_route_feedback (list<Route*>& routes)
}
}
-bool
-GenericMidiControlProtocol::active() const
-{
- return _port && send();
-}
-
diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.h b/libs/surfaces/generic_midi/generic_midi_control_protocol.h
index 54831b2982..ec789815f1 100644
--- a/libs/surfaces/generic_midi/generic_midi_control_protocol.h
+++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.h
@@ -14,9 +14,7 @@ class GenericMidiControlProtocol : public ControlProtocol {
GenericMidiControlProtocol (Session&);
virtual ~GenericMidiControlProtocol();
- int init ();
-
- bool active() const;
+ int set_active (bool yn);
void set_port (MIDI::Port*);
MIDI::Port* port () const { return _port; }
diff --git a/libs/surfaces/generic_midi/interface.cc b/libs/surfaces/generic_midi/interface.cc
index 500d745deb..8fb50cdaae 100644
--- a/libs/surfaces/generic_midi/interface.cc
+++ b/libs/surfaces/generic_midi/interface.cc
@@ -9,7 +9,7 @@ new_generic_midi_protocol (ControlProtocolDescriptor* descriptor, Session* s)
{
GenericMidiControlProtocol* gmcp = new GenericMidiControlProtocol (*s);
- if (gmcp->init ()) {
+ if (gmcp->set_active (true)) {
delete gmcp;
return 0;
}
diff --git a/libs/surfaces/tranzport/SConscript b/libs/surfaces/tranzport/SConscript
index e43f0bc77c..2739f17614 100644
--- a/libs/surfaces/tranzport/SConscript
+++ b/libs/surfaces/tranzport/SConscript
@@ -46,6 +46,8 @@ Default(libardour_tranzport)
if env['NLS']:
i18n (tranzport, tranzport_files, env)
+env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2/surfaces'), libardour_tranzport))
+
env.Alias('tarball', env.Distribute (env['DISTTREE'],
[ 'SConscript', 'i18n.h', 'gettext.h' ] +
tranzport_files +
diff --git a/libs/surfaces/tranzport/interface.cc b/libs/surfaces/tranzport/interface.cc
index f2160c3144..ab9c93348b 100644
--- a/libs/surfaces/tranzport/interface.cc
+++ b/libs/surfaces/tranzport/interface.cc
@@ -9,7 +9,7 @@ new_tranzport_protocol (ControlProtocolDescriptor* descriptor, Session* s)
{
TranzportControlProtocol* tcp = new TranzportControlProtocol (*s);
- if (tcp->init ()) {
+ if (tcp->set_active (true)) {
delete tcp;
return 0;
}
diff --git a/libs/surfaces/tranzport/tranzport_control_protocol.cc b/libs/surfaces/tranzport/tranzport_control_protocol.cc
index 6760caac63..a0f04443ef 100644
--- a/libs/surfaces/tranzport/tranzport_control_protocol.cc
+++ b/libs/surfaces/tranzport/tranzport_control_protocol.cc
@@ -1,7 +1,29 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program 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.
+
+ This program 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 more 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
#include <iostream>
#include <algorithm>
+#include <float.h>
#include <sys/time.h>
+#include <errno.h>
#include <pbd/pthread_utils.h>
@@ -19,8 +41,16 @@ using namespace sigc;
#include "i18n.h"
+#include <pbd/abstract_ui.cc>
+
+BaseUI::RequestType LEDChange = BaseUI::new_request_type ();
+BaseUI::RequestType Print = BaseUI::new_request_type ();
+BaseUI::RequestType SetCurrentTrack = BaseUI::new_request_type ();
+
TranzportControlProtocol::TranzportControlProtocol (Session& s)
- : ControlProtocol (s, _("Tranzport"))
+ : ControlProtocol (s, X_("Tranzport")),
+ AbstractUI<TranzportRequest> (X_("Tranzport"), false)
+
{
timeout = 60000;
buttonmask = 0;
@@ -34,91 +64,78 @@ TranzportControlProtocol::TranzportControlProtocol (Session& s)
wheel_shift_mode = WheelShiftGain;
timerclear (&last_wheel_motion);
last_wheel_dir = 1;
+ last_track_gain = FLT_MAX;
display_mode = DisplayNormal;
- requested_display_mode = display_mode;
memset (current_screen, 0, sizeof (current_screen));
+ memset (pending_screen, 0, sizeof (pending_screen));
for (uint32_t i = 0; i < sizeof(lights)/sizeof(lights[0]); ++i) {
lights[i] = false;
}
- session.RecordStateChanged.connect (mem_fun (*this, &TranzportControlProtocol::record_status_changed));
+ for (uint32_t i = 0; i < sizeof(pending_lights)/sizeof(pending_lights[0]); ++i) {
+ pending_lights[i] = false;
+ }
}
TranzportControlProtocol::~TranzportControlProtocol ()
{
- if (udev) {
- pthread_cancel_one (thread);
- lcd_clear ();
- close ();
- }
+ set_active (false);
}
int
-TranzportControlProtocol::init ()
+TranzportControlProtocol::set_active (bool yn)
{
- if (open ()) {
- return -1;
- }
+ if (yn != _active) {
- lcd_clear ();
- lights_off ();
-
- show_wheel_mode();
- next_track ();
- show_transport_time ();
+ if (yn) {
- /* outbound thread */
+ if (open ()) {
+ return -1;
+ }
+
+ if (pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this) == 0) {
+ _active = true;
+ } else {
+ return -1;
+ }
- init_thread ();
+ } else {
- /* inbound thread */
-
- pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this);
+ pthread_cancel_one (thread);
+ lcd_clear ();
+ close ();
+ _active = false;
+ }
+ }
return 0;
}
-bool
-TranzportControlProtocol::active() const
-{
- return true;
-}
-
void
-TranzportControlProtocol::send_route_feedback (list<Route*>& routes)
+TranzportControlProtocol::show_track_gain ()
{
+ if (current_route) {
+ gain_t g = current_route->gain();
+ if (g != last_track_gain) {
+ char buf[16];
+ snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (current_route->gain()));
+ print (0, 9, buf);
+ last_track_gain = g;
+ }
+ } else {
+ print (0, 9, " ");
+ }
}
void
-TranzportControlProtocol::send_global_feedback ()
+TranzportControlProtocol::normal_update ()
{
- if (requested_display_mode != display_mode) {
- switch (requested_display_mode) {
- case DisplayNormal:
- enter_normal_display_mode ();
- break;
- case DisplayBigMeter:
- enter_big_meter_mode ();
- break;
- }
- }
-
- switch (display_mode) {
- case DisplayBigMeter:
- show_meter ();
- break;
-
- case DisplayNormal:
- show_transport_time ();
- if (session.soloing()) {
- light_on (LightAnysolo);
- } else {
- light_off (LightAnysolo);
- }
- break;
- }
+ show_current_track ();
+ show_transport_time ();
+ show_track_gain ();
+ show_wheel_mode ();
}
void
@@ -126,11 +143,11 @@ TranzportControlProtocol::next_display_mode ()
{
switch (display_mode) {
case DisplayNormal:
- requested_display_mode = DisplayBigMeter;
+ display_mode = DisplayBigMeter;
break;
case DisplayBigMeter:
- requested_display_mode = DisplayNormal;
+ display_mode = DisplayNormal;
break;
}
}
@@ -147,11 +164,14 @@ TranzportControlProtocol::enter_big_meter_mode ()
void
TranzportControlProtocol::enter_normal_display_mode ()
{
+ last_where += 1; /* force time redisplay */
+ last_track_gain = FLT_MAX; /* force gain redisplay */
+
lcd_clear ();
lights_off ();
show_current_track ();
show_wheel_mode ();
- last_where += 1; /* force time redisplay */
+ show_wheel_mode ();
show_transport_time ();
display_mode = DisplayNormal;
}
@@ -205,7 +225,7 @@ TranzportControlProtocol::show_meter ()
uint32_t fill = (uint32_t) floor (fraction * 40);
char buf[21];
- int i;
+ uint32_t i;
if (fill == last_meter_fill) {
/* nothing to do */
@@ -286,20 +306,6 @@ TranzportControlProtocol::_monitor_work (void* arg)
return static_cast<TranzportControlProtocol*>(arg)->monitor_work ();
}
-void*
-TranzportControlProtocol::monitor_work ()
-{
- PBD::ThreadCreated (pthread_self(), X_("tranzport monitor"));
-
- while (true) {
- if (read ()) {
- break;
- }
- }
-
- return 0;
-}
-
int
TranzportControlProtocol::open ()
{
@@ -366,6 +372,7 @@ TranzportControlProtocol::close ()
if (usb_close (udev)) {
error << _("Tranzport: cannot close device") << endmsg;
+ udev = 0;
ret = 0;
}
@@ -377,10 +384,7 @@ TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
{
int val;
- {
- LockMonitor lm (write_lock, __LINE__, __FILE__);
- val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
- }
+ val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
if (val < 0)
return val;
@@ -405,29 +409,56 @@ TranzportControlProtocol::lcd_clear ()
cmd[6] = ' ';
cmd[7] = 0x00;
- {
- LockMonitor lp (print_lock, __LINE__, __FILE__);
- LockMonitor lw (write_lock, __LINE__, __FILE__);
-
- for (uint8_t i = 0; i < 10; ++i) {
- cmd[2] = i;
- usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, 500);
- }
-
- memset (current_screen, ' ', sizeof (current_screen));
+ for (uint8_t i = 0; i < 10; ++i) {
+ cmd[2] = i;
+ usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, 500);
}
+
+ memset (current_screen, ' ', sizeof (current_screen));
+ memset (pending_screen, ' ', sizeof (pending_screen));
}
void
TranzportControlProtocol::lights_off ()
{
- light_off (LightRecord);
- light_off (LightTrackrec);
- light_off (LightTrackmute);
- light_off (LightTracksolo);
- light_off (LightAnysolo);
- light_off (LightLoop);
- light_off (LightPunch);
+ uint8_t cmd[8];
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x00;
+ cmd[3] = 0x00;
+ cmd[4] = 0x00;
+ cmd[5] = 0x00;
+ cmd[6] = 0x00;
+ cmd[7] = 0x00;
+
+ cmd[2] = LightRecord;
+ if (write (cmd, 500) == 0) {
+ lights[LightRecord] = false;
+ }
+ cmd[2] = LightTrackrec;
+ if (write (cmd, 500) == 0) {
+ lights[LightTrackrec] = false;
+ }
+ cmd[2] = LightTrackmute;
+ if (write (cmd, 500) == 0) {
+ lights[LightTrackmute] = false;
+ }
+ cmd[2] = LightTracksolo;
+ if (write (cmd, 500) == 0) {
+ lights[LightTracksolo] = false;
+ }
+ cmd[2] = LightAnysolo;
+ if (write (cmd, 500) == 0) {
+ lights[LightAnysolo] = false;
+ }
+ cmd[2] = LightLoop;
+ if (write (cmd, 500) == 0) {
+ lights[LightLoop] = false;
+ }
+ cmd[2] = LightPunch;
+ if (write (cmd, 500) == 0) {
+ lights[LightPunch] = false;
+ }
}
int
@@ -486,25 +517,250 @@ TranzportControlProtocol::light_off (LightID light)
}
}
-int
-TranzportControlProtocol::read (uint32_t timeout_override)
+void*
+TranzportControlProtocol::monitor_work ()
{
+ struct sched_param rtparam;
+ int err;
uint8_t buf[8];
int val;
- memset(buf, 0, 8);
- again:
- val = usb_interrupt_read(udev, READ_ENDPOINT, (char*) buf, 8, timeout_override ? timeout_override : timeout);
- if (val < 0) {
- return val;
+ PBD::ThreadCreated (pthread_self(), X_("Tranzport"));
+
+ memset (&rtparam, 0, sizeof (rtparam));
+ rtparam.sched_priority = 3; /* XXX should be relative to audio (JACK) thread */
+
+ if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
+ // do we care? not particularly.
+ info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), BaseUI::name(), strerror (errno)) << endmsg;
+ }
+
+ pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
+ pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
+
+ /* set initial state */
+
+ lcd_clear ();
+ lights_off ();
+
+ show_wheel_mode();
+ next_track ();
+ show_transport_time ();
+
+ while (true) {
+
+ /* bInterval for this beastie is 10ms */
+
+ pthread_testcancel();
+ usleep (20000);
+ pthread_testcancel();
+
+ /* anything to read ? */
+
+ val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 0);
+
+ /* any requests to handle? */
+
+ handle_ui_requests ();
+
+ if (val == 8) {
+ process (buf);
+ }
+
+ /* update whatever needs updating */
+
+ update_state ();
}
- if (val != 8) {
- if (val == 0) {
- goto again;
+
+ return (void*) 0;
+}
+
+int
+TranzportControlProtocol::update_state ()
+{
+ int row;
+ int col_base;
+ int col;
+ int cell;
+
+ /* do the text updates */
+
+ switch (display_mode) {
+ case DisplayBigMeter:
+ show_meter ();
+ break;
+
+ case DisplayNormal:
+ normal_update ();
+ break;
+ }
+
+ /* next: flush LCD */
+
+ cell = 0;
+
+ for (row = 0; row < 2; ++row) {
+
+ for (col_base = 0, col = 0; col < 20; ) {
+
+ if (pending_screen[row][col] != current_screen[row][col]) {
+
+ /* something in this cell is different, so dump the cell
+ to the device.
+ */
+
+ uint8_t cmd[8];
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x01;
+ cmd[2] = cell;
+ cmd[3] = pending_screen[row][col_base];
+ cmd[4] = pending_screen[row][col_base+1];
+ cmd[5] = pending_screen[row][col_base+2];
+ cmd[6] = pending_screen[row][col_base+3];
+ cmd[7] = 0x00;
+
+ if (usb_interrupt_write (udev, WRITE_ENDPOINT, (char *) cmd, 8, 500) == 8) {
+ /* successful write: copy to current */
+ memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
+ }
+
+ /* skip the rest of the 4 character cell since we wrote+copied it already */
+
+ col_base += 4;
+ col = col_base;
+ cell++;
+
+ } else {
+
+ col++;
+
+ if (col && col % 4 == 0) {
+ cell++;
+ col_base += 4;
+ }
+ }
}
- return -1;
}
+ /* now update LED's */
+
+ /* per track */
+
+ if (current_route) {
+ AudioTrack* at = dynamic_cast<AudioTrack*> (current_route);
+ if (at && at->record_enabled()) {
+ pending_lights[LightTrackrec] = true;
+ } else {
+ pending_lights[LightTrackrec] = false;
+ }
+ if (current_route->muted()) {
+ pending_lights[LightTrackmute] = true;
+ } else {
+ pending_lights[LightTrackmute] = false;
+ }
+ if (current_route->soloed()) {
+ pending_lights[LightTracksolo] = true;
+ } else {
+ pending_lights[LightTracksolo] = false;
+ }
+
+ } else {
+ pending_lights[LightTrackrec] = false;
+ pending_lights[LightTracksolo] = false;
+ pending_lights[LightTrackmute] = false;
+ }
+
+ /* global */
+
+ if (session.get_auto_loop()) {
+ pending_lights[LightLoop] = true;
+ } else {
+ pending_lights[LightLoop] = false;
+ }
+
+ if (session.get_punch_in() || session.get_punch_out()) {
+ pending_lights[LightPunch] = true;
+ } else {
+ pending_lights[LightPunch] = false;
+ }
+
+ if (session.get_record_enabled()) {
+ pending_lights[LightRecord] = true;
+ } else {
+ pending_lights[LightRecord] = false;
+ }
+
+ if (session.soloing ()) {
+ pending_lights[LightAnysolo] = true;
+ } else {
+ pending_lights[LightAnysolo] = false;
+ }
+
+ /* flush changed light change */
+
+ if (pending_lights[LightRecord] != lights[LightRecord]) {
+ if (pending_lights[LightRecord]) {
+ light_on (LightRecord);
+ } else {
+ light_off (LightRecord);
+ }
+ }
+
+ if (pending_lights[LightTracksolo] != lights[LightTracksolo]) {
+ if (pending_lights[LightTracksolo]) {
+ light_on (LightTracksolo);
+ } else {
+ light_off (LightTracksolo);
+ }
+ }
+
+ if (pending_lights[LightTrackmute] != lights[LightTrackmute]) {
+ if (pending_lights[LightTrackmute]) {
+ light_on (LightTrackmute);
+ } else {
+ light_off (LightTrackmute);
+ }
+ }
+
+ if (pending_lights[LightTracksolo] != lights[LightTracksolo]) {
+ if (pending_lights[LightTracksolo]) {
+ light_on (LightTracksolo);
+ } else {
+ light_off (LightTracksolo);
+ }
+ }
+
+ if (pending_lights[LightAnysolo] != lights[LightAnysolo]) {
+ if (pending_lights[LightAnysolo]) {
+ light_on (LightAnysolo);
+ } else {
+ light_off (LightAnysolo);
+ }
+ }
+
+ if (pending_lights[LightLoop] != lights[LightLoop]) {
+ if (pending_lights[LightLoop]) {
+ light_on (LightLoop);
+ } else {
+ light_off (LightLoop);
+ }
+ }
+
+ if (pending_lights[LightPunch] != lights[LightPunch]) {
+ if (pending_lights[LightPunch]) {
+ light_on (LightPunch);
+ } else {
+ light_off (LightPunch);
+ }
+ }
+
+ return 0;
+}
+
+int
+TranzportControlProtocol::process (uint8_t* buf)
+{
// printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
uint32_t this_button_mask;
@@ -672,87 +928,14 @@ TranzportControlProtocol::read (uint32_t timeout_override)
void
TranzportControlProtocol::show_current_track ()
{
- for (vector<sigc::connection>::iterator i = track_connections.begin(); i != track_connections.end(); ++i) {
- (*i).disconnect ();
- }
- track_connections.clear ();
-
if (current_route == 0) {
print (0, 0, "--------");
- return;
- }
-
- string name = current_route->name();
-
- print (0, 0, name.substr (0, 8).c_str());
-
- track_solo_changed (0);
- track_mute_changed (0);
- track_rec_changed (0);
-
- track_connections.push_back (current_route->solo_changed.connect (mem_fun (*this, &TranzportControlProtocol::track_solo_changed)));
- track_connections.push_back (current_route->mute_changed.connect (mem_fun (*this, &TranzportControlProtocol::track_mute_changed)));
- track_connections.push_back (current_route->record_enable_changed.connect (mem_fun (*this, &TranzportControlProtocol::track_rec_changed)));
- track_connections.push_back (current_route->gain_changed.connect (mem_fun (*this, &TranzportControlProtocol::track_gain_changed)));
-}
-
-void
-TranzportControlProtocol::record_status_changed ()
-{
- if (session.get_record_enabled()) {
- light_on (LightRecord);
- } else {
- light_off (LightRecord);
- }
-}
-
-void
-TranzportControlProtocol::track_gain_changed (void* ignored)
-{
- char buf[8];
-
- switch (display_mode) {
- case DisplayNormal:
- snprintf (buf, sizeof (buf), "%.1fdB", coefficient_to_dB (current_route->gain()));
- print (0, 9, buf);
- break;
- default:
- break;
- }
-}
-
-void
-TranzportControlProtocol::track_solo_changed (void* ignored)
-{
- if (current_route->soloed()) {
- light_on (LightTracksolo);
- } else {
- light_off (LightTracksolo);
- }
-}
-
-void
-TranzportControlProtocol::track_mute_changed (void *ignored)
-{
- if (current_route->muted()) {
- light_on (LightTrackmute);
} else {
- light_off (LightTrackmute);
+ print (0, 0, current_route->name().substr (0, 8).c_str());
}
}
void
-TranzportControlProtocol::track_rec_changed (void *ignored)
-{
- if (current_route->record_enabled()) {
- light_on (LightTrackrec);
- } else {
- light_off (LightTrackrec);
- }
-}
-
-
-void
TranzportControlProtocol::button_event_battery_press (bool shifted)
{
}
@@ -798,11 +981,7 @@ void
TranzportControlProtocol::button_event_trackrec_press (bool shifted)
{
if (shifted) {
- if (session.get_record_enabled()) {
- session.record_disenable_all ();
- } else {
- session.record_enable_all ();
- }
+ toggle_all_rec_enables ();
} else {
if (current_route) {
AudioTrack* at = dynamic_cast<AudioTrack*>(current_route);
@@ -855,9 +1034,9 @@ void
TranzportControlProtocol::button_event_undo_press (bool shifted)
{
if (shifted) {
- session.redo (1);
+ redo ();
} else {
- session.undo (1);
+ undo ();
}
}
@@ -907,6 +1086,8 @@ TranzportControlProtocol::button_event_loop_press (bool shifted)
{
if (shifted) {
next_wheel_shift_mode ();
+ } else {
+ loop_toggle ();
}
}
@@ -933,8 +1114,7 @@ TranzportControlProtocol::button_event_prev_release (bool shifted)
void
TranzportControlProtocol::button_event_add_press (bool shifted)
{
- jack_nframes_t when = session.audible_frame();
- session.locations()->add (new Location (when, when, _("unnamed"), Location::IsMark));
+ add_marker ();
}
void
@@ -961,9 +1141,9 @@ void
TranzportControlProtocol::button_event_rewind_press (bool shifted)
{
if (shifted) {
- session.goto_start ();
+ goto_start ();
} else {
- session.request_transport_speed (-2.0f);
+ rewind ();
}
}
@@ -976,9 +1156,10 @@ void
TranzportControlProtocol::button_event_fastforward_press (bool shifted)
{
if (shifted) {
- session.goto_end();
+ goto_end ();
} else {
- session.request_transport_speed (2.0f);}
+ ffwd ();
+ }
}
void
@@ -992,7 +1173,7 @@ TranzportControlProtocol::button_event_stop_press (bool shifted)
if (shifted) {
next_display_mode ();
} else {
- session.request_transport_speed (0.0);
+ transport_stop ();
}
}
@@ -1004,7 +1185,7 @@ TranzportControlProtocol::button_event_stop_release (bool shifted)
void
TranzportControlProtocol::button_event_play_press (bool shifted)
{
- session.request_transport_speed (1.0);
+ transport_play ();
}
void
@@ -1016,22 +1197,9 @@ void
TranzportControlProtocol::button_event_record_press (bool shifted)
{
if (shifted) {
- session.save_state ("");
+ save_state ();
} else {
- switch (session.record_status()) {
- case Session::Disabled:
- if (session.ntracks() == 0) {
- // string txt = _("Please create 1 or more track\nbefore trying to record.\nCheck the Session menu.");
- // MessageDialog msg (*editor, txt);
- // msg.run ();
- return;
- }
- session.maybe_enable_record ();
- break;
- case Session::Recording:
- case Session::Enabled:
- session.disable_record (true);
- }
+ rec_enable_toggle ();
}
}
@@ -1139,7 +1307,7 @@ TranzportControlProtocol::scrub ()
if (dir != last_wheel_dir) {
/* changed direction, start over */
- speed = 1.0f;
+ speed = 0.1f;
} else {
if (timerisset (&last_wheel_motion)) {
@@ -1160,7 +1328,7 @@ TranzportControlProtocol::scrub ()
last_wheel_motion = now;
last_wheel_dir = dir;
- session.request_transport_speed (speed * dir);
+ move_at (speed * dir);
}
void
@@ -1246,33 +1414,11 @@ TranzportControlProtocol::next_wheel_mode ()
}
void
-TranzportControlProtocol::next_marker ()
-{
- Location *location = session.locations()->first_location_after (session.transport_frame());
-
- if (location) {
- session.request_locate (location->start(), session.transport_rolling());
- } else {
- session.request_locate (session.current_end_frame());
- }
-}
-
-void
-TranzportControlProtocol::prev_marker ()
-{
- Location *location = session.locations()->first_location_before (session.transport_frame());
-
- if (location) {
- session.request_locate (location->start(), session.transport_rolling());
- } else {
- session.goto_start ();
- }
-}
-
-void
TranzportControlProtocol::next_track ()
{
uint32_t limit = session.nroutes();
+ uint32_t start = current_track_id;
+ Route* cr = current_route;
if (current_track_id == limit) {
current_track_id = 0;
@@ -1281,7 +1427,7 @@ TranzportControlProtocol::next_track ()
}
while (current_track_id < limit) {
- if ((current_route = session.route_by_remote_id (current_track_id)) != 0) {
+ if ((cr = session.route_by_remote_id (current_track_id)) != 0) {
break;
}
current_track_id++;
@@ -1289,14 +1435,24 @@ TranzportControlProtocol::next_track ()
if (current_track_id == limit) {
current_track_id = 0;
+ while (current_track_id != start) {
+ if ((cr = session.route_by_remote_id (current_track_id)) != 0) {
+ break;
+ }
+ current_track_id++;
+ }
}
- show_current_track ();
+ current_route = cr;
}
void
TranzportControlProtocol::prev_track ()
{
+ uint32_t limit = session.nroutes() - 1;
+ uint32_t start = current_track_id;
+ Route* cr = current_route;
+
if (current_track_id == 0) {
current_track_id = session.nroutes() - 1;
} else {
@@ -1304,17 +1460,37 @@ TranzportControlProtocol::prev_track ()
}
while (current_track_id >= 0) {
- if ((current_route = session.route_by_remote_id (current_track_id)) != 0) {
+ if ((cr = session.route_by_remote_id (current_track_id)) != 0) {
break;
}
current_track_id--;
}
if (current_track_id < 0) {
- current_track_id = 0;
+ current_track_id = limit;
+ while (current_track_id > start) {
+ if ((cr = session.route_by_remote_id (current_track_id)) != 0) {
+ break;
+ }
+ current_track_id--;
+ }
}
- show_current_track ();
+ current_route = cr;
+}
+
+void
+TranzportControlProtocol::set_current_track (Route* r)
+{
+ TranzportRequest* req = get_request (SetCurrentTrack);
+
+ if (req == 0) {
+ return;
+ }
+
+ req->track = r;
+
+ send_request (req);
}
void
@@ -1390,46 +1566,38 @@ TranzportControlProtocol::print (int row, int col, const char *text)
int offset = col % 4;
- {
-
- LockMonitor lm (print_lock, __LINE__, __FILE__);
-
- /* copy current cell contents into tmp */
-
- memcpy (tmp, &current_screen[row][base_col], 4);
-
- /* overwrite with new text */
-
- uint32_t tocopy = min ((4U - offset), left);
-
- memcpy (tmp+offset, text, tocopy);
-
- uint8_t cmd[8];
-
- /* compare with current screen */
-
- if (memcmp (tmp, &current_screen[row][base_col], 4)) {
-
- /* different, so update */
-
- memcpy (&current_screen[row][base_col], tmp, 4);
-
- cmd[0] = 0x00;
- cmd[1] = 0x01;
- cmd[2] = cell + (row * 5);
- cmd[3] = tmp[0];
- cmd[4] = tmp[1];
- cmd[5] = tmp[2];
- cmd[6] = tmp[3];
- cmd[7] = 0x00;
-
- write (cmd, 500);
- }
-
- text += tocopy;
- left -= tocopy;
- col += tocopy;
- }
+ /* copy current cell contents into tmp */
+
+ memcpy (tmp, &pending_screen[row][base_col], 4);
+
+ /* overwrite with new text */
+
+ uint32_t tocopy = min ((4U - offset), left);
+
+ memcpy (tmp+offset, text, tocopy);
+
+ /* copy it back to pending */
+
+ memcpy (&pending_screen[row][base_col], tmp, 4);
+
+ text += tocopy;
+ left -= tocopy;
+ col += tocopy;
}
}
+bool
+TranzportControlProtocol::caller_is_ui_thread ()
+{
+ return (pthread_self() == thread);
+}
+
+void
+TranzportControlProtocol::do_request (TranzportRequest* req)
+{
+ if (req->type == SetCurrentTrack) {
+ current_route = req->track;
+ }
+
+ return;
+}
diff --git a/libs/surfaces/tranzport/tranzport_control_protocol.h b/libs/surfaces/tranzport/tranzport_control_protocol.h
index 925ac72e2a..cf4a9a7d3f 100644
--- a/libs/surfaces/tranzport/tranzport_control_protocol.h
+++ b/libs/surfaces/tranzport/tranzport_control_protocol.h
@@ -10,18 +10,29 @@
#include <ardour/control_protocol.h>
#include <ardour/types.h>
-namespace ARDOUR {
+#include <pbd/abstract_ui.h>
+
+extern BaseUI::RequestType LEDChange;
+extern BaseUI::RequestType Print;
+extern BaseUI::RequestType SetCurrentTrack;
+
+struct TranzportRequest : public BaseUI::BaseRequestObject {
+ int led;
+ int row;
+ int col;
+ char* text;
+ ARDOUR::Route* track;
+};
-class TranzportControlProtocol : public ControlProtocol {
+class TranzportControlProtocol : public ARDOUR::ControlProtocol, public AbstractUI<TranzportRequest>
+{
public:
- TranzportControlProtocol (Session&);
+ TranzportControlProtocol (ARDOUR::Session&);
virtual ~TranzportControlProtocol();
- int init ();
- bool active() const;
+ int set_active (bool yn);
- void send_route_feedback (std::list<Route*>&);
- void send_global_feedback ();
+ bool caller_is_ui_thread();
private:
static const int VENDORID = 0x165b;
@@ -89,29 +100,32 @@ class TranzportControlProtocol : public ControlProtocol {
uint8_t _datawheel;
uint8_t _device_status;
usb_dev_handle* udev;
- Route* current_route;
+ ARDOUR::Route* current_route;
uint32_t current_track_id;
- char current_screen[2][20];
- bool lights[7];
WheelMode wheel_mode;
WheelShiftMode wheel_shift_mode;
- struct timeval last_wheel_motion;
- int last_wheel_dir;
DisplayMode display_mode;
- DisplayMode requested_display_mode;
- uint32_t last_meter_fill;
-
- std::vector<sigc::connection> track_connections;
- bool last_negative;
- uint32_t last_hrs;
- uint32_t last_mins;
- uint32_t last_secs;
- uint32_t last_frames;
+ void do_request (TranzportRequest*);
+
+ PBD::Lock update_lock;
+ char current_screen[2][20];
+ char pending_screen[2][20];
+ bool lights[7];
+ bool pending_lights[7];
+
+ bool last_negative;
+ uint32_t last_hrs;
+ uint32_t last_mins;
+ uint32_t last_secs;
+ uint32_t last_frames;
jack_nframes_t last_where;
+ ARDOUR::gain_t last_track_gain;
+ uint32_t last_meter_fill;
+ struct timeval last_wheel_motion;
+ int last_wheel_dir;
- PBD::Lock write_lock;
- PBD::Lock print_lock;
+ PBD::Lock io_lock;
int open ();
int read (uint32_t timeout_override = 0);
@@ -128,21 +142,19 @@ class TranzportControlProtocol : public ControlProtocol {
void enter_big_meter_mode ();
void enter_normal_display_mode ();
+
void next_display_mode ();
+ void normal_update ();
+
void show_current_track ();
+ void show_track_gain ();
void show_transport_time ();
void show_wheel_mode ();
void show_gain ();
void show_pan ();
void show_meter ();
- void track_solo_changed (void*);
- void track_rec_changed (void*);
- void track_mute_changed (void*);
- void track_gain_changed (void*);
- void record_status_changed ();
-
void datawheel ();
void scrub ();
void scroll ();
@@ -151,10 +163,9 @@ class TranzportControlProtocol : public ControlProtocol {
void next_wheel_mode ();
void next_wheel_shift_mode ();
+ void set_current_track (ARDOUR::Route*);
void next_track ();
void prev_track ();
- void next_marker ();
- void prev_marker ();
void step_gain_up ();
void step_gain_down ();
void step_pan_right ();
@@ -203,8 +214,10 @@ class TranzportControlProtocol : public ControlProtocol {
void button_event_play_release (bool shifted);
void button_event_record_press (bool shifted);
void button_event_record_release (bool shifted);
+
+ int process (uint8_t *);
+ int update_state();
};
-} // namespace
#endif // ardour_tranzport_control_protocol_h