diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2006-04-24 22:45:19 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2006-04-24 22:45:19 +0000 |
commit | 028e1ebc4a392572cae586d0e9044a32b867cba4 (patch) | |
tree | 36d3a748486feb3f41575708bef8b153fef2cad4 /libs | |
parent | 484debb45c5ea45bccf0f9cb05b1239a9c2244a3 (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
Diffstat (limited to 'libs')
30 files changed, 1165 insertions, 1051 deletions
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 (¤t_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, ¤t_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, ¤t_screen[row][base_col], 4)) { - - /* different, so update */ - - memcpy (¤t_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 |