mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-16 11:46:25 +01:00
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
This commit is contained in:
parent
484debb45c
commit
028e1ebc4a
38 changed files with 1218 additions and 1084 deletions
31
SConstruct
31
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')
|
||||
|
|
@ -403,6 +402,21 @@ libraries['fst'] = LibraryInfo()
|
|||
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))
|
||||
|
|
|
|||
|
|
@ -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\\\"")
|
||||
|
||||
|
|
|
|||
|
|
@ -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,14 +473,20 @@ 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;
|
||||
|
||||
ActionManager::register_toggle_action (editor->editor_actions, action_name.c_str(), action_label.c_str(),
|
||||
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)));
|
||||
|
||||
Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
|
||||
|
||||
if ((*i)->protocol || (*i)->requested) {
|
||||
tact->set_active ();
|
||||
}
|
||||
|
||||
ui += "<menuitem action='";
|
||||
ui += action_name;
|
||||
ui += "'/>\n";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;\
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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\\\"")
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = "" );
|
||||
|
|
|
|||
34
libs/ardour/ardour/basic_ui.h
Normal file
34
libs/ardour/ardour/basic_ui.h
Normal file
|
|
@ -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__ */
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
std::string name() const { return _name; }
|
||||
|
||||
virtual int set_active (bool yn) = 0;
|
||||
bool get_active() const { return _active; }
|
||||
|
||||
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; }
|
||||
|
||||
bool send() const { return _send != 0; }
|
||||
bool send_route_feedback () const { return _send & SendRoute; }
|
||||
bool send_global_feedback () const { return _send & SendGlobal; }
|
||||
|
||||
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" {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
189
libs/ardour/basic_ui.cc
Normal file
189
libs/ardour/basic_ui.cc
Normal file
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,6 +270,11 @@ ARDOUR::init (AudioEngine& engine, bool use_vst, bool try_optimization, void (*s
|
|||
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);
|
||||
|
||||
return 0;
|
||||
|
|
@ -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 = "")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -946,6 +946,10 @@ 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 */
|
||||
|
|
@ -953,10 +957,6 @@ Session::set_slave_source (SlaveSource src, jack_nframes_t frame)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (src == _slave_type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if (src == JACK && Config->get_jack_time_master()) {
|
||||
// return -1;
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -261,52 +269,10 @@ UI::touch_display (Touchable *display)
|
|||
send_request (req);
|
||||
}
|
||||
|
||||
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,7 +303,7 @@ UI::set_state (Widget *w, StateType state)
|
|||
void
|
||||
UI::idle_add (int (*func)(void *), void *arg)
|
||||
{
|
||||
Request *req = get_request (AddIdle);
|
||||
UIRequest *req = get_request (AddIdle);
|
||||
|
||||
if (req == 0) {
|
||||
return;
|
||||
|
|
@ -349,124 +315,8 @@ UI::idle_add (int (*func)(void *), void *arg)
|
|||
send_request (req);
|
||||
}
|
||||
|
||||
void
|
||||
UI::timeout_add (unsigned int timeout, int (*func)(void *), void *arg)
|
||||
{
|
||||
Request *req = get_request (AddTimeout);
|
||||
|
||||
if (req == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
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,147 +330,45 @@ UI::signal_pipe_callback (void *arg, int fd, GdkInputCondition cond)
|
|||
}
|
||||
|
||||
void
|
||||
UI::handle_ui_requests ()
|
||||
UI::do_request (UIRequest* req)
|
||||
{
|
||||
RequestBufferMap::iterator i;
|
||||
if (req->type == ErrorMessage) {
|
||||
|
||||
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 ();
|
||||
}
|
||||
|
||||
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:
|
||||
} else if (req->type == CallSlot) {
|
||||
|
||||
req->slot ();
|
||||
break;
|
||||
|
||||
case CallSlotLocked:
|
||||
pthread_mutex_lock (&req->slot_lock);
|
||||
req->slot ();
|
||||
pthread_cond_signal (&req->slot_cond);
|
||||
pthread_mutex_unlock (&req->slot_lock);
|
||||
break;
|
||||
} else if (req->type == TouchDisplay) {
|
||||
|
||||
case 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:
|
||||
} else if (req->type == SetTip) {
|
||||
|
||||
/* XXX need to figure out how this works */
|
||||
break;
|
||||
|
||||
case AddIdle:
|
||||
gtk_idle_add (req->function, req->arg);
|
||||
break;
|
||||
} else {
|
||||
|
||||
case AddTimeout:
|
||||
gtk_timeout_add (req->timeout, req->function, req->arg);
|
||||
break;
|
||||
|
||||
default:
|
||||
error << "UI: unknown request type "
|
||||
error << "GtkUI: 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);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
UI::request (RequestType rt)
|
||||
{
|
||||
Request *req = get_request (rt);
|
||||
|
||||
if (req == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
send_request (req);
|
||||
}
|
||||
|
||||
/*======================================================================
|
||||
Error Display
|
||||
======================================================================*/
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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')))
|
||||
|
|
|
|||
87
libs/pbd3/base_ui.cc
Normal file
87
libs/pbd3/base_ui.cc
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
|
||||
/* needed to be a receiver ... */
|
||||
void call_slot (sigc::slot<void> el_slot) {
|
||||
RequestObject *req = get_request (BaseUI::CallSlot);
|
||||
|
||||
virtual void receive (Transmitter::Channel, const char *) = 0;
|
||||
if (req == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
req->slot = el_slot;
|
||||
send_request (req);
|
||||
}
|
||||
|
||||
void register_thread (pthread_t, std::string);
|
||||
void register_thread_with_request_count (pthread_t, std::string, uint32_t num_requests);
|
||||
|
||||
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__ */
|
||||
|
||||
|
||||
|
|
|
|||
46
libs/pbd3/pbd/base_ui.h
Normal file
46
libs/pbd3/pbd/base_ui.h
Normal file
|
|
@ -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__ */
|
||||
|
|
@ -31,7 +31,7 @@ using std::vector;
|
|||
|
||||
class strstream;
|
||||
|
||||
class Receiver : public sigc::trackable
|
||||
class Receiver : virtual public sigc::trackable
|
||||
{
|
||||
public:
|
||||
Receiver ();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 +
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 +
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,103 +64,90 @@ 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 (yn != _active) {
|
||||
|
||||
if (yn) {
|
||||
|
||||
if (open ()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this) == 0) {
|
||||
_active = true;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
pthread_cancel_one (thread);
|
||||
lcd_clear ();
|
||||
lights_off ();
|
||||
|
||||
show_wheel_mode();
|
||||
next_track ();
|
||||
show_transport_time ();
|
||||
|
||||
/* outbound thread */
|
||||
|
||||
init_thread ();
|
||||
|
||||
/* inbound thread */
|
||||
|
||||
pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this);
|
||||
close ();
|
||||
_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
TranzportControlProtocol::active() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::send_route_feedback (list<Route*>& routes)
|
||||
TranzportControlProtocol::show_track_gain ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::send_global_feedback ()
|
||||
{
|
||||
if (requested_display_mode != display_mode) {
|
||||
switch (requested_display_mode) {
|
||||
case DisplayNormal:
|
||||
enter_normal_display_mode ();
|
||||
break;
|
||||
case DisplayBigMeter:
|
||||
enter_big_meter_mode ();
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
switch (display_mode) {
|
||||
case DisplayBigMeter:
|
||||
show_meter ();
|
||||
break;
|
||||
|
||||
case DisplayNormal:
|
||||
show_transport_time ();
|
||||
if (session.soloing()) {
|
||||
light_on (LightAnysolo);
|
||||
} else {
|
||||
light_off (LightAnysolo);
|
||||
}
|
||||
break;
|
||||
print (0, 9, " ");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::normal_update ()
|
||||
{
|
||||
show_current_track ();
|
||||
show_transport_time ();
|
||||
show_track_gain ();
|
||||
show_wheel_mode ();
|
||||
}
|
||||
|
||||
void
|
||||
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);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (val != 8) {
|
||||
if (val == 0) {
|
||||
goto again;
|
||||
}
|
||||
return -1;
|
||||
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 ();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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,86 +928,13 @@ 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);
|
||||
print (0, 0, current_route->name().substr (0, 8).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -1245,34 +1413,12 @@ TranzportControlProtocol::next_wheel_mode ()
|
|||
show_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,13 +1566,9 @@ 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);
|
||||
memcpy (tmp, &pending_screen[row][base_col], 4);
|
||||
|
||||
/* overwrite with new text */
|
||||
|
||||
|
|
@ -1404,32 +1576,28 @@ TranzportControlProtocol::print (int row, int col, const char *text)
|
|||
|
||||
memcpy (tmp+offset, text, tocopy);
|
||||
|
||||
uint8_t cmd[8];
|
||||
/* copy it back to pending */
|
||||
|
||||
/* 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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,18 +10,29 @@
|
|||
#include <ardour/control_protocol.h>
|
||||
#include <ardour/types.h>
|
||||
|
||||
namespace ARDOUR {
|
||||
#include <pbd/abstract_ui.h>
|
||||
|
||||
class TranzportControlProtocol : public ControlProtocol {
|
||||
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 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,19 +100,19 @@ 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;
|
||||
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;
|
||||
|
|
@ -109,9 +120,12 @@ class TranzportControlProtocol : public ControlProtocol {
|
|||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue