major design changes: use glib event loop for MIDI thread/UI; rework design of BaseUI...
authorPaul Davis <paul@linuxaudiosystems.com>
Wed, 9 Dec 2009 03:05:14 +0000 (03:05 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Wed, 9 Dec 2009 03:05:14 +0000 (03:05 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@6328 d708f5d6-7413-0410-9779-e7cbd77b26cf

50 files changed:
gtk2_ardour/ardour_ui.cc
gtk2_ardour/ardour_ui2.cc
gtk2_ardour/editor_audio_import.cc
gtk2_ardour/editor_ops.cc
gtk2_ardour/editor_timefx.cc
gtk2_ardour/gui_thread.h
gtk2_ardour/main.cc
gtk2_ardour/rc_option_editor.cc
gtk2_ardour/route_ui.cc
gtk2_ardour/route_ui.h
gtk2_ardour/sfdb_ui.cc
libs/ardour/analyser.cc
libs/ardour/ardour/midi_ui.h [new file with mode: 0644]
libs/ardour/ardour/session.h
libs/ardour/audioengine.cc
libs/ardour/butler.cc
libs/ardour/export_channel_configuration.cc
libs/ardour/midi_ui.cc [new file with mode: 0644]
libs/ardour/session.cc
libs/ardour/session_midi.cc
libs/ardour/session_process.cc
libs/ardour/session_state.cc
libs/ardour/source_factory.cc
libs/ardour/wscript
libs/gtkmm2ext/gtk_ui.cc
libs/gtkmm2ext/gtkmm2ext/gtk_ui.h
libs/gtkmm2ext/gtkmm2ext/stateful_button.h
libs/gtkmm2ext/stateful_button.cc
libs/midi++2/coremidi_midiport.cc
libs/midi++2/jack_midiport.cc
libs/midi++2/manager.cc
libs/midi++2/midi++/jack.h
libs/midi++2/midi++/manager.h
libs/midi++2/midi++/port.h
libs/pbd/base_ui.cc
libs/pbd/crossthread.cc [new file with mode: 0644]
libs/pbd/pbd/abstract_ui.cc
libs/pbd/pbd/abstract_ui.h
libs/pbd/pbd/base_ui.h
libs/pbd/pbd/crossthread.h
libs/pbd/pbd/pthread_utils.h
libs/pbd/pthread_utils.cc
libs/pbd/wscript
libs/surfaces/control_protocol/basic_ui.cc
libs/surfaces/control_protocol/control_protocol/basic_ui.h
libs/surfaces/frontier/tranzport/tranzport_control_protocol.cc
libs/surfaces/mackie/mackie_control_protocol.cc
libs/surfaces/osc/osc.cc
libs/surfaces/powermate/powermate.cc
libs/surfaces/tranzport/init.cc

index 28dd679f1052379ae7b5844af8ad720573a356a1..a4675f4a212b92384e42a211b8bfaf5f4ecf21ce 100644 (file)
@@ -115,7 +115,7 @@ sigc::signal<void,nframes_t, bool, nframes_t> ARDOUR_UI::Clock;
 
 ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
 
-       : Gtkmm2ext::UI (X_("Ardour"), argcp, argvp),
+       : Gtkmm2ext::UI (X_("gui"), argcp, argvp),
 
          primary_clock (X_("primary"), false, X_("TransportClockDisplay"), true, true, false, true),
          secondary_clock (X_("secondary"), false, X_("SecondaryClockDisplay"), true, true, false, true),
index b54fe9225a729c0e0003b728d3f6bfc97b9e4296..d12ed9a817fa751157160ec5f95d3d5cdcbc7851 100644 (file)
@@ -478,7 +478,7 @@ void
 ARDOUR_UI::solo_alert_toggle ()
 {
        if (session) {
-               session->set_all_solo (!session->soloing());
+               session->set_solo (session->get_routes(), !session->soloing());
        }
 }
 
index f8b6d7c141ca5294fc8a0ed04573402cb0d80562..fb3828ab5375fc9985c8a1a013bb3c6dbc40ed19 100644 (file)
@@ -491,7 +491,7 @@ Editor::import_sndfiles (vector<ustring> paths, ImportMode mode, SrcQuality qual
           (the GUI) to direct additional steps after that.
        */
 
-       pthread_create_and_store ("import", &import_status.thread, 0, _import_thread, this);
+       pthread_create_and_store ("import", &import_status.thread, _import_thread, this);
        pthread_detach (import_status.thread);
 
        while (!import_status.done && !import_status.cancel) {
@@ -879,7 +879,6 @@ Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t
 void *
 Editor::_import_thread (void *arg)
 {
-       PBD::notify_gui_about_thread_creation (pthread_self(), X_("Import"));
        SessionEvent::create_per_thread_pool ("import events", 64);
 
        Editor *ed = (Editor *) arg;
index 833ac580a2b262809ab1c5d882436d91b7a22a79..449ea4213241ad3092262d553e6a064c74944ec8 100644 (file)
@@ -3653,7 +3653,6 @@ Editor::unfreeze_route ()
 void*
 Editor::_freeze_thread (void* arg)
 {
-       PBD::notify_gui_about_thread_creation (pthread_self(), X_("Freeze"));
        SessionEvent::create_per_thread_pool ("freeze events", 64);
 
        return static_cast<Editor*>(arg)->freeze_thread ();
@@ -3702,13 +3701,7 @@ Editor::freeze_route ()
        itt.cancel = false;
        itt.progress = 0.0f;
 
-       pthread_attr_t attr;
-       pthread_attr_init(&attr);
-       pthread_attr_setstacksize(&attr, 500000);
-
-       pthread_create_and_store (X_("freezer"), &itt.thread, &attr, _freeze_thread, this);
-
-       pthread_attr_destroy(&attr);
+       pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
 
        track_canvas->get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
 
index b52121f9c528a78b940c211ab76dadefe578dde6..65c8a833e8d80885e054c6ed36e3d788d214d4ed 100644 (file)
@@ -225,7 +225,7 @@ Editor::time_fx (RegionSelection& regions, float val, bool pitching)
        current_timefx->first_delete = current_timefx->signal_delete_event().connect
                (mem_fun (current_timefx, &TimeFXDialog::delete_in_progress));
 
-       if (pthread_create_and_store ("timefx", &current_timefx->request.thread, 0, timefx_thread, current_timefx)) {
+       if (pthread_create_and_store ("timefx", &current_timefx->request.thread, timefx_thread, current_timefx)) {
                current_timefx->hide ();
                error << _("timefx cannot be started - thread creation error") << endmsg;
                return -1;
@@ -337,7 +337,6 @@ Editor::do_timefx (TimeFXDialog& dialog)
 void*
 Editor::timefx_thread (void *arg)
 {
-       PBD::notify_gui_about_thread_creation (pthread_self(), X_("TimeFX"));
        SessionEvent::create_per_thread_pool ("timefx events", 64);
 
        TimeFXDialog* tsd = static_cast<TimeFXDialog*>(arg);
index 388902fa28be8c315fa4504e31423ca9567208e4..f9e5165a030f3f3e1cd69d61e1891663732a79f9 100644 (file)
 #define __ardour_gtk_gui_thread_h__
 
 #include <gtkmm2ext/gtk_ui.h>
-#include "pbd/crossthread.h"
 
 #define ENSURE_GUI_THREAD(slot) \
-     if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) {\
+     if (!Gtkmm2ext::UI::instance()->caller_is_self()) { \
        Gtkmm2ext::UI::instance()->call_slot ((slot));\
         return;\
      }
 
-#define GTK_SAFE(theSlot) crossthread_safe (Gtkmm2ext::UI::instance()->thread_id(),\
-                                           *Gtkmm2ext::UI::instance(), \
-                                           (theSlot))
-
 #endif /* __ardour_gtk_gui_thread_h__ */
index fdea4cb754d1bf38ca414b417cc09650cf7caa0f..406ad714e94cbc074a27ae1ed7af2cee3418d971 100644 (file)
@@ -393,7 +393,8 @@ int main (int argc, char *argv[])
        ui = 0;
 
        ARDOUR::cleanup ();
-       pthread_cancel_all ();
+       // pthread_cancel ();
+
 #ifdef HAVE_LV2
        close_external_ui_windows();
 #endif
index 8597995a88b103a0dc5f9f4f3cdee8615a9f0ba7..c8956961c5d642a51782e48d36710cfb23f805dc 100644 (file)
@@ -183,6 +183,7 @@ private:
                node.add_property ("mode", smod);
 
                if (MIDI::Manager::instance()->add_port (node) != 0) {
+                       cerr << " there are now " << MIDI::Manager::instance()->nports() << endl;
                        ports_changed ();
                }
        }
index 9a849257a29503150e4078e8156514175dd6daed..9eb356ddd17d3c0742b92c4a6b28ddd3636450dc 100644 (file)
@@ -262,18 +262,21 @@ RouteUI::mute_press(GdkEventButton* ev)
 
                                if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
 
+#if 0
                                        /* Primary-Tertiary-click applies change to all routes */
 
                                        _session.begin_reversible_command (_("mute change"));
                                         Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this);
-                                       _session.set_all_mute (!_route->muted());
+                                       _session.set_mute (!_route->muted());
                                         cmd->mark();
                                        _session.add_command(cmd);
                                        _session.commit_reversible_command ();
                                        multiple_mute_change = true;
+#endif
 
                                } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
 
+#if 0
                                        /* Primary-button1 applies change to the mix group.
                                           NOTE: Primary-button2 is MIDI learn.
                                        */
@@ -281,15 +284,18 @@ RouteUI::mute_press(GdkEventButton* ev)
                                        if (ev->button == 1) {
                                                set_route_group_mute (_route, !_route->muted());
                                        }
+#endif
 
                                } else {
 
+#if 0
                                        /* plain click applies change to this route */
                                        if (wait_for_release) {
                                                _route->set_mute (!_route->muted(), this);
                                        } else {
                                                reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this);
                                        }
+#endif
                                }
                        }
                }
@@ -318,6 +324,18 @@ RouteUI::mute_release(GdkEventButton*)
        return true;
 }
 
+void
+RouteUI::post_solo_cleanup (SessionEvent* ev, bool was_not_latched)
+{
+       ENSURE_GUI_THREAD (bind (mem_fun (*this, &RouteUI::post_solo_cleanup), ev, was_not_latched));
+
+       delete ev;
+       
+       if (was_not_latched) {
+               Config->set_solo_latched (false);
+       }
+}      
+
 bool
 RouteUI::solo_press(GdkEventButton* ev)
 {
@@ -364,6 +382,7 @@ RouteUI::solo_press(GdkEventButton* ev)
 
                                                /* Primary-Tertiary-click applies change to all routes */
                                                bool was_not_latched = false;
+
                                                if (!Config->get_solo_latched ()) {
                                                        was_not_latched = true;
                                                        /*
@@ -373,28 +392,25 @@ RouteUI::solo_press(GdkEventButton* ev)
                                                        */
                                                        Config->set_solo_latched (true);
                                                }
-                                               _session.begin_reversible_command (_("solo change"));
-                                               Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
-                                               _session.set_all_solo (!_route->soloed());
-                                               cmd->mark();
-                                               _session.add_command (cmd);
-                                               _session.commit_reversible_command ();
-                                               multiple_solo_change = true;
-                                               if (was_not_latched) {
-                                                       Config->set_solo_latched (false);
-                                               }
+
+                                               SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
+                                               ev->rt_slot =   bind (sigc::mem_fun (_session, &Session::set_solo), _session.get_routes(), !_route->soloed());
+                                               ev->rt_return = bind (sigc::mem_fun (*this, &RouteUI::post_solo_cleanup), was_not_latched);
+                                               
+                                               _session.queue_event (ev);
 
                                        } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
 
                                                // Primary-Secondary-click: exclusively solo this track, not a toggle */
+                                               
+                                               //boost::shared_ptr<RouteList> rl (new RouteList);
+                                               //rl->push_back (route());
 
-                                               _session.begin_reversible_command (_("solo change"));
-                                               Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this);
-                                               _session.set_all_solo (false);
-                                               _route->set_solo (true, this);
-                                               cmd->mark();
-                                               _session.add_command(cmd);
-                                               _session.commit_reversible_command ();
+                                               //SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
+                                               // ev->rt_slot =  bind (sigc::mem_fun (_session, &Session::set_just_one_solo), rl, true);
+                                               //ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
+                                               
+                                               //_session.queue_event (ev);
 
                                        } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
 
@@ -405,22 +421,29 @@ RouteUI::solo_press(GdkEventButton* ev)
 
                                        } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
 
+#if 0
                                                /* Primary-button1: solo mix group.
                                                   NOTE: Primary-button2 is MIDI learn.
                                                */
 
                                                if (ev->button == 1) {
-                                                       set_route_group_solo (_route, !_route->soloed());
+                                                       queue_route_group_op (RouteGroup::Solo, &Session::set_all_solo, !_route->soloed());
                                                }
+#endif
+
 
                                        } else {
 
                                                /* click: solo this route */
-                                               if (wait_for_release) {
-                                                       _route->set_solo (!_route->soloed(), this);
-                                               } else {
-                                                       reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
-                                               }
+
+                                               boost::shared_ptr<RouteList> rl (new RouteList);
+                                               rl->push_back (route());
+                                               
+                                               SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
+                                               ev->rt_slot =   bind (sigc::mem_fun (_session, &Session::set_solo), rl, !rec_enable_button->get_active());
+                                               ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
+                                               
+                                               _session.queue_event (ev);
                                        }
                                }
                        }
@@ -521,7 +544,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
                } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
 
                        SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
-                       ev->rt_slot =   bind (sigc::mem_fun (_session, &Session::set_all_record_enable), _session.get_routes(), !rec_enable_button->get_active());
+                       ev->rt_slot =   bind (sigc::mem_fun (_session, &Session::set_record_enable), _session.get_routes(), !rec_enable_button->get_active());
                        ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
                        
                        _session.queue_event (ev);
@@ -533,7 +556,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
                        */
                        
                        if (ev->button == 1) {
-                               queue_route_group_op (RouteGroup::RecEnable, &Session::set_all_record_enable, !rec_enable_button->get_active());
+                               queue_route_group_op (RouteGroup::RecEnable, &Session::set_record_enable, !rec_enable_button->get_active());
                        }
 
                } else if (Keyboard::is_context_menu_event (ev)) {
@@ -543,9 +566,9 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
                } else {
                        boost::shared_ptr<RouteList> rl (new RouteList);
                        rl->push_back (route());
-
+                       
                        SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
-                       ev->rt_slot =   bind (sigc::mem_fun (_session, &Session::set_all_record_enable), rl, !rec_enable_button->get_active());
+                       ev->rt_slot =   bind (sigc::mem_fun (_session, &Session::set_record_enable), rl, !rec_enable_button->get_active());
                        ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
 
                        _session.queue_event (ev);
index 275366cbe6804eff3346e211271eaca43848d063..90bdaa7fb7cc1cc6a46aeca2c3c7387807a83d35 100644 (file)
@@ -217,6 +217,7 @@ class RouteUI : public virtual AxisView
 
        void post_rtop_cleanup (ARDOUR::SessionEvent* ev);
        void post_group_rtop_cleanup (ARDOUR::SessionEvent* ev, ARDOUR::RouteGroup*, ARDOUR::RouteGroup::Property);
+       void post_solo_cleanup (ARDOUR::SessionEvent* ev, bool was_not_latched);
 };
 
 #endif /* __ardour_route_ui__ */
index aa8d7b77f2a24164e23731b26964e9d624bc12ee..c719bb65159935a3303c8d39d57d241da9d8ee5f 100644 (file)
@@ -733,7 +733,6 @@ SoundFileBrowser::found_search_clicked ()
 void*
 freesound_search_thread_entry (void* arg)
 {
-       PBD::notify_gui_about_thread_creation (pthread_self(), X_("Freesound Search"));
        SessionEvent::create_per_thread_pool ("freesound events", 64);
 
        static_cast<SoundFileBrowser*>(arg)->freesound_search_thread ();
@@ -757,7 +756,7 @@ SoundFileBrowser::freesound_search_clicked ()
                searching = true;
                freesound_search_btn.set_label(_("Cancel"));
                pthread_t freesound_thr;
-               pthread_create_and_store ("freesound_search", &freesound_thr, 0, freesound_search_thread_entry, this);
+               pthread_create_and_store ("freesound_search", &freesound_thr, freesound_search_thread_entry, this);
        }
 }
 
index 8e2ec99f27f65739389603ed74754356f078d654..992e5c52467f55ecc540f911cc6c623849064378 100644 (file)
@@ -76,7 +76,6 @@ Analyser::queue_source_for_analysis (boost::shared_ptr<Source> src, bool force)
 void
 Analyser::work ()
 {
-       PBD::notify_gui_about_thread_creation (pthread_self(), string ("analyser-") + to_string (pthread_self(), std::dec));
        SessionEvent::create_per_thread_pool ("Analyser", 64);
 
        while (true) {
diff --git a/libs/ardour/ardour/midi_ui.h b/libs/ardour/ardour/midi_ui.h
new file mode 100644 (file)
index 0000000..e1f322d
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef __libardour_midi_ui_h__
+#define __libardour_midi_ui_h__
+
+#include <list>
+#include "pbd/abstract_ui.h"
+
+namespace MIDI { 
+       class port;
+}
+
+namespace ARDOUR {
+
+class Session;
+
+/* this is mostly a placeholder because I suspect that at some
+   point we will want to add more members to accomodate
+   certain types of requests to the MIDI UI
+*/
+
+struct MidiUIRequest : public BaseUI::BaseRequestObject {
+  public:
+       MidiUIRequest () {}
+       ~MidiUIRequest() {}
+};
+
+class MidiControlUI : public AbstractUI<MidiUIRequest>
+{
+  public:
+       MidiControlUI (Session& s);
+       ~MidiControlUI ();
+       
+       static BaseUI::RequestType PortChange;
+       
+       void change_midi_ports ();
+       
+  protected:
+       void thread_init ();
+       void do_request (MidiUIRequest*);
+       
+  private:
+       typedef std::list<Glib::RefPtr<Glib::IOSource> > PortSources;
+       PortSources port_sources;
+       ARDOUR::Session& _session;
+       
+       bool midi_input_handler (Glib::IOCondition, MIDI::Port*);
+       void reset_ports ();
+       void clear_ports ();
+};
+
+}
+
+#endif /* __libardour_midi_ui_h__ */
index ed34c387e8806b322908d02458828788a8ace2c9..4e1721bae00c72358fccbc2148804d27ad1d8da5 100644 (file)
@@ -97,6 +97,7 @@ class MidiDiskstream;
 class MidiRegion;
 class MidiSource;
 class MidiTrack;
+class MidiControlUI;
 class NamedSelection;
 class Playlist;
 class PluginInsert;
@@ -616,14 +617,14 @@ class Session : public PBD::StatefulDestructible, public SessionEventManager, pu
        bool soloing() const { return _non_soloed_outs_muted; }
        bool listening() const { return _listen_cnt > 0; }
 
-       void set_all_solo (bool);
-       void set_all_mute (bool);
-       void set_all_listen (bool);
+       void set_solo (boost::shared_ptr<RouteList>, bool);
+       void set_mute (boost::shared_ptr<RouteList>, bool);
+       void set_listen (boost::shared_ptr<RouteList>, bool);
 
        sigc::signal<void,bool> SoloActive;
        sigc::signal<void> SoloChanged;
        
-       void set_all_record_enable (boost::shared_ptr<RouteList>, bool);
+       void set_record_enable (boost::shared_ptr<RouteList>, bool);
 
        /* control/master out */
 
@@ -1253,25 +1254,11 @@ class Session : public PBD::StatefulDestructible, public SessionEventManager, pu
        bool non_realtime_work_pending() const { return static_cast<bool>(post_transport_work()); }
        bool process_can_proceed() const { return !(post_transport_work() & ProcessCannotProceedMask); }
 
-       struct MIDIRequest {
-               enum Type {
-                       PortChange,
-                       Quit
-               };
-               Type type;
-       };
-
-       Glib::Mutex  midi_lock;
-       pthread_t    midi_thread;
-       int          midi_request_pipe[2];
-       RingBuffer<MIDIRequest*> midi_requests;
+       MidiControlUI* midi_control_ui;
 
        int           start_midi_thread ();
        void          terminate_midi_thread ();
-       void          poke_midi_thread ();
-       static void *_midi_thread_work (void *arg);
-       void          midi_thread_work ();
-       void          change_midi_ports ();
+
        int           use_config_midi_ports ();
 
        void set_play_loop (bool yn);
index b69b06c4dbe4eebc1e6ae5479d3a5fb2fc1d5717..2c16c52296a0bb8b0b0b2215ef5d97e3e3db2df7 100644 (file)
@@ -135,7 +135,9 @@ _thread_init_callback (void * /*arg*/)
           knows about it.
        */
 
-       PBD::notify_gui_about_thread_creation (pthread_self(), X_("Audioengine"), 4096);
+       PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("Audioengine"), 4096);
+       PBD::notify_gui_about_thread_creation ("midiui", pthread_self(), X_("Audioengine"), 128);
+
        SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
 
        MIDI::JACK_MidiPort::set_process_thread (pthread_self());
index 76a7fb2424fdf84fafae0df29dfc081508993304..05ac7316ec1af3f9b2a7e9e53e16ccc140a9bb59 100644 (file)
@@ -89,7 +89,7 @@ Butler::start_thread()
                return -1;
        }
 
-       if (pthread_create_and_store ("disk butler", &thread, 0, _thread_work, this)) {
+       if (pthread_create_and_store ("disk butler", &thread, _thread_work, this)) {
                error << _("Session: could not create butler thread") << endmsg;
                return -1;
        }
@@ -113,7 +113,6 @@ Butler::terminate_thread ()
 void *
 Butler::_thread_work (void* arg)
 {
-       PBD::notify_gui_about_thread_creation (pthread_self(), X_("Butler"));
        SessionEvent::create_per_thread_pool ("butler events", 64);
        return ((Butler *) arg)->thread_work ();
 }
index 047aadc671c55fbba4a58c12b74e870c1e6181d3..89a4952febb083ab51c29a6c4caa0d0e95de1740 100644 (file)
@@ -176,7 +176,6 @@ ExportChannelConfiguration::write_file ()
 void *
 ExportChannelConfiguration::_write_files (void *arg)
 {
-       notify_gui_about_thread_creation (pthread_self(), "Export post-processing");
        SessionEvent::create_per_thread_pool ("exporter events", 64);
 
        // cc can be trated like 'this'
diff --git a/libs/ardour/midi_ui.cc b/libs/ardour/midi_ui.cc
new file mode 100644 (file)
index 0000000..5a0640b
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+  Copyright (C) 2009 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.
+
+*/
+#include <cstdlib>
+
+#include "pbd/pthread_utils.h"
+
+#include "midi++/manager.h"
+#include "midi++/port.h"
+
+#include "ardour/debug.h"
+#include "ardour/audioengine.h"
+#include "ardour/midi_ui.h"
+#include "ardour/session.h"
+#include "ardour/session_event.h"
+#include "ardour/types.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace Glib;
+
+#include "i18n.h"
+
+BaseUI::RequestType MidiControlUI::PortChange = BaseUI::new_request_type();
+
+#include "pbd/abstract_ui.cc"  /* instantiate the template */
+
+MidiControlUI::MidiControlUI (Session& s)
+       : AbstractUI<MidiUIRequest> (_("midiui"))
+       , _session (s) 
+{
+       MIDI::Manager::instance()->PortsChanged.connect (mem_fun (*this, &MidiControlUI::change_midi_ports));
+}
+
+MidiControlUI::~MidiControlUI ()
+{
+       clear_ports ();
+}
+
+void
+MidiControlUI::do_request (MidiUIRequest* req)
+{
+       if (req->type == PortChange) {
+
+               /* restart event loop with new ports */
+               DEBUG_TRACE (DEBUG::MidiIO, "reset ports\n");
+               reset_ports ();
+
+       } else if (req->type == CallSlot) {
+
+               req->the_slot ();
+       }
+}
+
+void
+MidiControlUI::change_midi_ports ()
+{
+       MidiUIRequest* req = get_request (PortChange);
+       if (req == 0) {
+               return;
+       }
+       send_request (req);
+}
+
+bool
+MidiControlUI::midi_input_handler (IOCondition ioc, MIDI::Port* port)
+{
+       if (ioc & ~IO_IN) {
+               return false;
+       }
+
+       if (ioc & IO_IN) {
+
+               if (port->must_drain_selectable()) {
+                       CrossThreadChannel::drain (port->selectable());
+               }
+
+               DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name()));
+               nframes64_t now = _session.engine().frame_time();
+               port->parse (now);
+       }
+
+       return true;
+}
+
+void
+MidiControlUI::clear_ports ()
+{
+       for (PortSources::iterator i = port_sources.begin(); i != port_sources.end(); ++i) {
+               /* remove existing sources from the event loop */
+               (*i)->destroy ();
+       }
+
+       port_sources.clear ();
+}
+
+void
+MidiControlUI::reset_ports ()
+{
+       clear_ports ();
+
+       MIDI::Manager::PortList plist = MIDI::Manager::instance()->get_midi_ports ();
+
+       for (MIDI::Manager::PortList::iterator i = plist.begin(); i != plist.end(); ++i) {
+               int fd;
+               if ((fd = (*i)->selectable ()) >= 0) {
+                       Glib::RefPtr<IOSource> psrc = IOSource::create (fd, IO_IN|IO_HUP|IO_ERR);
+                       psrc->connect (bind (mem_fun (*this, &MidiControlUI::midi_input_handler), (*i)));
+                       port_sources.push_back (psrc);
+               } 
+       }
+
+       for (PortSources::iterator i = port_sources.begin(); i != port_sources.end(); ++i) {
+               (*i)->attach (_main_loop->get_context());
+       }
+}
+
+void
+MidiControlUI::thread_init ()
+{      
+       struct sched_param rtparam;
+
+       PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self(), X_("MIDI"), 2048);
+       SessionEvent::create_per_thread_pool (X_("MIDI I/O"), 128);
+
+       memset (&rtparam, 0, sizeof (rtparam));
+       rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
+
+       if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
+               // do we care? not particularly.
+       }
+
+       reset_ports ();
+}
+
index cdd5041856b93c870f90d1f1ae743c1d5fd8aeac..9c3b28049f98a45cc8a43f849296ba38c5039b03 100644 (file)
@@ -71,6 +71,7 @@
 #include "ardour/midi_playlist.h"
 #include "ardour/midi_region.h"
 #include "ardour/midi_track.h"
+#include "ardour/midi_ui.h"
 #include "ardour/named_selection.h"
 #include "ardour/playlist.h"
 #include "ardour/plugin_insert.h"
@@ -137,8 +138,6 @@ Session::Session (AudioEngine &eng,
          _butler (new Butler (this)),
          _post_transport_work (0),
          _send_timecode_update (false),
-         midi_thread (pthread_t (0)),
-         midi_requests (128), // the size of this should match the midi request pool size
          diskstreams (new DiskstreamList),
          routes (new RouteList),
          _total_free_4k_blocks (0),
@@ -224,8 +223,6 @@ Session::Session (AudioEngine &eng,
          _butler (new Butler (this)),
          _post_transport_work (0),
          _send_timecode_update (false),
-         midi_thread (pthread_t (0)),
-         midi_requests (16),
          diskstreams (new DiskstreamList),
          routes (new RouteList),
          _total_free_4k_blocks (0),
@@ -366,7 +363,8 @@ Session::destroy ()
        Stateful::loading_state_version = 0;
 
        _butler->terminate_thread ();
-       //terminate_midi_thread ();
+       
+       delete midi_control_ui;
 
        if (click_data != default_click) {
                delete [] click_data;
@@ -3518,10 +3516,8 @@ Session::is_auditioning () const
 }
 
 void
-Session::set_all_solo (bool yn)
+Session::set_solo (boost::shared_ptr<RouteList> r, bool yn)
 {
-       shared_ptr<RouteList> r = routes.reader ();
-
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if (!(*i)->is_hidden()) {
                        (*i)->set_solo (yn, this);
@@ -3532,10 +3528,8 @@ Session::set_all_solo (bool yn)
 }
 
 void
-Session::set_all_listen (bool yn)
+Session::set_listen (boost::shared_ptr<RouteList> r, bool yn)
 {
-       shared_ptr<RouteList> r = routes.reader ();
-
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if (!(*i)->is_hidden()) {
                        (*i)->set_listen (yn, this);
@@ -3546,10 +3540,8 @@ Session::set_all_listen (bool yn)
 }
 
 void
-Session::set_all_mute (bool yn)
+Session::set_mute (boost::shared_ptr<RouteList> r, bool yn)
 {
-       shared_ptr<RouteList> r = routes.reader ();
-
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if (!(*i)->is_hidden()) {
                        (*i)->set_mute (yn, this);
@@ -3605,7 +3597,7 @@ Session::graph_reordered ()
 }
 
 void
-Session::set_all_record_enable (boost::shared_ptr<RouteList> rl, bool yn)
+Session::set_record_enable (boost::shared_ptr<RouteList> rl, bool yn)
 {
        if (!writable()) {
                return;
@@ -4037,8 +4029,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
                return result;
        }
 
-       // any bigger than this seems to cause stack overflows in called functions
-       const nframes_t chunk_size = (128 * 1024)/4;
+       const nframes_t chunk_size = (256 * 1024)/4;
 
        // block all process callback handling
 
@@ -4323,9 +4314,9 @@ Session::solo_control_mode_changed ()
        /* cancel all solo or all listen when solo control mode changes */
 
        if (Config->get_solo_control_is_listen_control()) {
-               set_all_solo (false);
+               set_solo (routes.reader(), false);
        } else {
-               set_all_listen (false);
+               set_listen (routes.reader(), false);
        }
 }
 
index a0366dd6e88f41e77f3188e2719f715207709ff0..4388fe6dca6d437fbe208315d36518d5d53e361a 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <boost/shared_ptr.hpp>
 
+#include <glibmm/main.h>
 
 #include "midi++/mmc.h"
 #include "midi++/port.h"
@@ -41,6 +42,7 @@
 #include "ardour/session.h"
 #include "ardour/audio_track.h"
 #include "ardour/midi_track.h"
+#include "ardour/midi_ui.h"
 #include "ardour/audio_diskstream.h"
 #include "ardour/slave.h"
 #include "ardour/cycles.h"
@@ -52,6 +54,7 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 using namespace MIDI;
+using namespace Glib;
 
 MachineControl::CommandSignature MMC_CommandSignature;
 MachineControl::ResponseSignature MMC_ResponseSignature;
@@ -146,7 +149,6 @@ Session::set_mtc_port (string port_tag)
 
   out:
        MTC_PortChanged(); /* EMIT SIGNAL */
-       change_midi_ports ();
        set_dirty();
        return 0;
 }
@@ -244,7 +246,6 @@ Session::set_mmc_port (string port_tag)
 
   out:
        MMC_PortChanged(); /* EMIT SIGNAL */
-       change_midi_ports ();
        set_dirty();
        return 0;
 }
@@ -278,7 +279,6 @@ Session::set_midi_port (string /*port_tag*/)
   out:
 #endif
        MIDI_PortChanged(); /* EMIT SIGNAL */
-       change_midi_ports ();
        set_dirty();
        return 0;
 }
@@ -320,7 +320,6 @@ Session::set_midi_clock_port (string port_tag)
 
   out:
        MIDIClock_PortChanged(); /* EMIT SIGNAL */
-       change_midi_ports ();
        set_dirty();
        return 0;
 }
@@ -640,8 +639,12 @@ Session::mmc_step (MIDI::MachineControl &/*mmc*/, int steps)
        last_mmc_step = now;
 
        if (!step_queued) {
-               midi_timeouts.push_back (mem_fun (*this, &Session::mmc_step_timeout));
-               step_queued = true;
+               if (midi_control_ui) {
+                       RefPtr<TimeoutSource> tsrc = TimeoutSource::create (100);
+                       tsrc->connect (mem_fun (*this, &Session::mmc_step_timeout));
+                       tsrc->attach (midi_control_ui->main_loop()->get_context());
+                       step_queued = true;
+               }
        }
 }
 
@@ -741,16 +744,6 @@ Session::mmc_record_enable (MIDI::MachineControl &mmc, size_t trk, bool enabled)
        }
 }
 
-void
-Session::change_midi_ports ()
-{
-       MIDIRequest* request = new MIDIRequest;
-
-       request->type = MIDIRequest::PortChange;
-       midi_requests.write (&request, 1);
-       poke_midi_thread ();
-}
-
 /** Send MTC Full Frame message (complete Timecode time) for the start of this cycle.
  * This resets the MTC code, the next quarter frame message that is sent will be
  * the first one with the beginning of this cycle as the new start point.
@@ -1022,495 +1015,17 @@ Session::mmc_step_timeout ()
 int
 Session::start_midi_thread ()
 {
-       if (pipe (midi_request_pipe)) {
-               error << string_compose(_("Cannot create transport request signal pipe (%1)"), strerror (errno)) << endmsg;
-               return -1;
-       }
-
-       if (fcntl (midi_request_pipe[0], F_SETFL, O_NONBLOCK)) {
-               error << string_compose(_("UI: cannot set O_NONBLOCK on "    "signal read pipe (%1)"), strerror (errno)) << endmsg;
-               return -1;
-       }
-
-       if (fcntl (midi_request_pipe[1], F_SETFL, O_NONBLOCK)) {
-               error << string_compose(_("UI: cannot set O_NONBLOCK on "    "signal write pipe (%1)"), strerror (errno)) << endmsg;
-               return -1;
-       }
-
-       if (pthread_create_and_store ("transport", &midi_thread, 0, _midi_thread_work, this)) {
-               error << _("Session: could not create transport thread") << endmsg;
-               return -1;
-       }
-
+       midi_control_ui = new MidiControlUI (*this);
+       midi_control_ui->run ();
        return 0;
 }
 
 void
 Session::terminate_midi_thread ()
 {
-       if (midi_thread) {
-
-               MIDIRequest* request = new MIDIRequest;
-               void* status;
-
-               request->type = MIDIRequest::Quit;
-
-               midi_requests.write (&request, 1);
-               poke_midi_thread ();
-
-               pthread_join (midi_thread, &status);
-       }
-}
-
-void
-Session::poke_midi_thread ()
-{
-       static char c = 0;
-
-       if (write (midi_request_pipe[1], &c, 1) != 1) {
-               error << string_compose(_("cannot send signal to midi thread! (%1)"), strerror (errno)) << endmsg;
-       }
-}
-
-void *
-Session::_midi_thread_work (void* arg)
-{
-       pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
-       pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
-
-       ((Session *) arg)->midi_thread_work ();
-       return 0;
-}
-
-#if 0
-void
-Session::midi_thread_work ()
-{
-       MIDIRequest* request;
-       GPollFD pfd[4];
-       int nfds = 0;
-       int timeout;
-       int fds_ready;
-       struct sched_param rtparam;
-       int x;
-       bool restart;
-       vector<MIDI::Port*> ports;
-
-       PBD::notify_gui_about_thread_creation (pthread_self(), X_("MIDI"), 2048);
-       SessionEvent::create_per_thread_pool (X_("MIDI I/O"), 128);
-
-       memset (&rtparam, 0, sizeof (rtparam));
-       rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
-
-       if ((x = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
-               // do we care? not particularly.
-       }
-
-       /* set up the port vector; 5 is the largest possible size for now */
-
-       ports.assign (5, (MIDI::Port*) 0);
-
-       GMainContext* main_context = g_main_context_new ();
-
-       while (1) {
-
-               nfds = 0;
-
-               gpfd[nfds].fd = midi_request_pipe[0];
-               gpfd[nfds].events = POLLIN|POLLHUP|POLLERR;
-               nfds++;
-
-               if (Config->get_mmc_control() && _mmc_port && _mmc_port->selectable() >= 0) {
-                       gpfd[nfds].fd = _mmc_port->selectable();
-                       gpfd[nfds].events = POLLIN|POLLHUP|POLLERR;
-                       ports[nfds] = _mmc_port;
-                       g_main_context_add_poll (&gpfd[nfds]);
-                       DEBUG_TRACE (DEBUG::MidiIO, string_compose ("set up port #%1 for mmc @ %2\n", nfds, _mmc_port));
-                       nfds++;
-               }
-
-               /* if MTC is being handled on a different port from MMC
-                  or we are not handling MMC at all, poll
-                  the relevant port.
-               */
-
-               if (_mtc_port && (_mtc_port != _mmc_port || !Config->get_mmc_control()) && _mtc_port->selectable() >= 0) {
-                       gpfd[nfds].fd = _mtc_port->selectable();
-                       gpfd[nfds].events = POLLIN|POLLHUP|POLLERR;
-                       ports[nfds] = _mtc_port;
-                       g_main_context_add_poll (&gpfd[nfds]);
-                       DEBUG_TRACE (DEBUG::MidiIO, string_compose ("set up port #%1 for mtc @ %2\n", nfds, _mtc_port));
-                       nfds++;
-               }
-
-               if (_midi_clock_port && (_midi_clock_port != _mmc_port || !Config->get_mmc_control()) && _midi_clock_port->selectable() >= 0) {
-                       gpfd[nfds].fd = _midi_clock_port->selectable();
-                       gpfd[nfds].events = POLLIN|POLLHUP|POLLERR;
-                       ports[nfds] = _midi_clock_port;
-                       g_main_context_add_poll (&gpfd[nfds]);
-                       DEBUG_TRACE (DEBUG::MidiIO, string_compose ("set up port #%1 for midi clock @ %2\n", nfds, _midi_clock_port));
-                       nfds++;
-               }
-
-               /* if we are using MMC control, we obviously have to listen
-                  the relevant port.
-               */
-
-               if (_midi_port && (_midi_port != _mmc_port || !Config->get_mmc_control()) && (_midi_port != _mtc_port) && _midi_port->selectable() >= 0) {
-                       gpfd[nfds].fd = _midi_port->selectable();
-                       gpfd[nfds].events = POLLIN|POLLHUP|POLLERR;
-                       ports[nfds] = _midi_port;
-                       g_main_context_add_poll (&gpfd[nfds]);
-                       DEBUG_TRACE (DEBUG::MidiIO, string_compose ("set up port #%1 for midi @ %2\n", nfds, _midi_port));
-                       nfds++;
-               }
-
-               if (!midi_timeouts.empty()) {
-                       timeout = 100; /* 10msecs */
-               } else {
-                       timeout = -1; /* if there is no data, we don't care */
-               }
-
-         again:
-
-               DEBUG_TRACE (DEBUG::MidiIO, string_compose ("MIDI poll on %1 fds for %2\n", nfds, timeout));
-               if (g_poll (gpfd, nfds, timeout) < 0) {
-                       if (errno == EINTR) {
-                               /* gdb at work, perhaps */
-                               goto again;
-                       }
-
-                       error << string_compose(_("MIDI thread poll failed (%1)"), strerror (errno)) << endmsg;
-
-                       break;
-               }
-
-               nframes64_t now = engine().frame_time();
-
-               DEBUG_TRACE (DEBUG::MidiIO, "MIDI thread awake\n");
-
-               fds_ready = 0;
-
-               /* check the transport request pipe */
-
-               if (gpfd[0].revents & ~POLLIN) {
-                       error << _("Error on transport thread request pipe") << endmsg;
-                       break;
-               }
-
-               if (gpfd[0].revents & POLLIN) {
-
-                       char foo[16];
-                       
-                       DEBUG_TRACE (DEBUG::MidiIO, "MIDI request FIFO ready\n");
-                       fds_ready++;
-
-                       /* empty the pipe of all current requests */
-
-                       while (1) {
-                               size_t nread = read (midi_request_pipe[0], &foo, sizeof (foo));
-
-                               if (nread > 0) {
-                                       if ((size_t) nread < sizeof (foo)) {
-                                               break;
-                                       } else {
-                                               continue;
-                                       }
-                               } else if (nread == 0) {
-                                       break;
-                               } else if (errno == EAGAIN) {
-                                       break;
-                               } else {
-                                       fatal << _("Error reading from transport request pipe") << endmsg;
-                                       /*NOTREACHED*/
-                               }
-                       }
-
-                       while (midi_requests.read (&request, 1) == 1) {
-
-                               switch (request->type) {
-                               case MIDIRequest::PortChange:
-                                       /* restart poll with new ports */
-                                       DEBUG_TRACE (DEBUG::MidiIO, "rebind\n");
-                                       restart = true;
-                                       break;
-
-                               case MIDIRequest::Quit:
-                                       delete request;
-                                       DEBUG_TRACE (DEBUG::MidiIO, "thread quit\n");
-                                       pthread_exit_pbd (0);
-                                       /*NOTREACHED*/
-                                       break;
-
-                               default:
-                                       break;
-                               }
-
-
-                               delete request;
-                       }
-
-               }
-
-               if (restart) {
-                       DEBUG_TRACE (DEBUG::MidiIO, "ports changed, restart poll\n");
-                       restart = false;
-                       continue;
-               }
-
-               /* now read the rest of the ports */
-
-               for (int p = 1; p < nfds; ++p) {
-
-                       DEBUG_STR_SET(foo, "port #%1 revents = ");
-                       DEBUG_STR(foo) << hex << pfd[p].revents << dec << endl;
-                       DEBUG_TRACE (DEBUG::MidiIO, string_compose (DEBUG_STR(foo).str(), p));
-
-                       if ((pfd[p].revents & ~POLLIN)) {
-                               // error << string_compose(_("Transport: error polling MIDI port %1 (revents =%2%3%4"), p, &hex, pfd[p].revents, &dec) << endmsg;
-                               break;
-                       }
-
-                       if (pfd[p].revents & POLLIN) {
-                               DEBUG_TRACE (DEBUG::MidiIO, string_compose ("MIDI fd # %1 has data ready @ %2\n", p, now));
-                               fds_ready++;
-                               ports[p]->parse (now);
-                       }
-
-                       g_main_context_remove_poll (&gpfd[p]);
-               }
-
-               /* timeout driven */
-
-               if (fds_ready < 2 && timeout != -1) {
-
-                       DEBUG_TRACE (DEBUG::MidiIO, "Check timeouts\n");
-                       for (MidiTimeoutList::iterator i = midi_timeouts.begin(); i != midi_timeouts.end(); ) {
-
-                               MidiTimeoutList::iterator tmp;
-                               tmp = i;
-                               ++tmp;
-
-                               if (!(*i)()) {
-                                       midi_timeouts.erase (i);
-                               }
-
-                               i = tmp;
-                       }
-               }
+       if (midi_control_ui) {
+               midi_control_ui->quit ();
        }
-
 }
-#endif
-
-void
-Session::midi_thread_work ()
-{
-       MIDIRequest* request;
-       struct pollfd pfd[4];
-       int nfds = 0;
-       int timeout;
-       int fds_ready;
-       struct sched_param rtparam;
-       int x;
-       bool restart;
-       vector<MIDI::Port*> ports;
-
-       PBD::notify_gui_about_thread_creation (pthread_self(), X_("MIDI"), 2048);
-       SessionEvent::create_per_thread_pool (X_("MIDI I/O"), 128);
-
-       memset (&rtparam, 0, sizeof (rtparam));
-       rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
-
-       if ((x = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
-               // do we care? not particularly.
-       }
-
-       /* set up the port vector; 5 is the largest possible size for now */
-
-       ports.assign (5, (MIDI::Port*) 0);
-
-       while (1) {
-
-               nfds = 0;
-
-               pfd[nfds].fd = midi_request_pipe[0];
-               pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
-               nfds++;
-
-               if (Config->get_mmc_control() && _mmc_port && _mmc_port->selectable() >= 0) {
-                       pfd[nfds].fd = _mmc_port->selectable();
-                       pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
-                       ports[nfds] = _mmc_port;
-                       DEBUG_TRACE (DEBUG::MidiIO, string_compose ("set up port #%1 for mmc @ %2\n", nfds, _mmc_port));
-                       nfds++;
-               }
-
-               /* if MTC is being handled on a different port from MMC
-                  or we are not handling MMC at all, poll
-                  the relevant port.
-               */
-
-               if (_mtc_port && (_mtc_port != _mmc_port || !Config->get_mmc_control()) && _mtc_port->selectable() >= 0) {
-                       pfd[nfds].fd = _mtc_port->selectable();
-                       pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
-                       ports[nfds] = _mtc_port;
-                       DEBUG_TRACE (DEBUG::MidiIO, string_compose ("set up port #%1 for mtc @ %2\n", nfds, _mtc_port));
-                       nfds++;
-               }
-
-               if (_midi_clock_port && (_midi_clock_port != _mmc_port || !Config->get_mmc_control()) && _midi_clock_port->selectable() >= 0) {
-                       pfd[nfds].fd = _midi_clock_port->selectable();
-                       pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
-                       ports[nfds] = _midi_clock_port;
-                       DEBUG_TRACE (DEBUG::MidiIO, string_compose ("set up port #%1 for midi clock @ %2\n", nfds, _midi_clock_port));
-                       nfds++;
-               }
-
-               /* if we are using MMC control, we obviously have to listen
-                  the relevant port.
-               */
-
-               if (_midi_port && (_midi_port != _mmc_port || !Config->get_mmc_control()) && (_midi_port != _mtc_port) && _midi_port->selectable() >= 0) {
-                       pfd[nfds].fd = _midi_port->selectable();
-                       pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
-                       ports[nfds] = _midi_port;
-                       DEBUG_TRACE (DEBUG::MidiIO, string_compose ("set up port #%1 for midi @ %2\n", nfds, _midi_port));
-                       nfds++;
-               }
-
-               if (!midi_timeouts.empty()) {
-                       timeout = 100; /* 10msecs */
-               } else {
-                       timeout = -1; /* if there is no data, we don't care */
-               }
-
-         again:
-               DEBUG_TRACE (DEBUG::MidiIO, string_compose ("MIDI poll on %1 fds for %2\n", nfds, timeout));
-               if (poll (pfd, nfds, timeout) < 0) {
-                       if (errno == EINTR) {
-                               /* gdb at work, perhaps */
-                               goto again;
-                       }
-
-                       error << string_compose(_("MIDI thread poll failed (%1)"), strerror (errno)) << endmsg;
-
-                       break;
-               }
-
-               nframes64_t now = engine().frame_time();
-
-               DEBUG_TRACE (DEBUG::MidiIO, "MIDI thread awake\n");
 
-               fds_ready = 0;
-
-               /* check the transport request pipe */
-
-               if (pfd[0].revents & ~POLLIN) {
-                       error << _("Error on transport thread request pipe") << endmsg;
-                       break;
-               }
-
-               if (pfd[0].revents & POLLIN) {
-
-                       char foo[16];
-                       
-                       DEBUG_TRACE (DEBUG::MidiIO, "MIDI request FIFO ready\n");
-                       fds_ready++;
-
-                       /* empty the pipe of all current requests */
-
-                       while (1) {
-                               size_t nread = read (midi_request_pipe[0], &foo, sizeof (foo));
-
-                               if (nread > 0) {
-                                       if ((size_t) nread < sizeof (foo)) {
-                                               break;
-                                       } else {
-                                               continue;
-                                       }
-                               } else if (nread == 0) {
-                                       break;
-                               } else if (errno == EAGAIN) {
-                                       break;
-                               } else {
-                                       fatal << _("Error reading from transport request pipe") << endmsg;
-                                       /*NOTREACHED*/
-                               }
-                       }
-
-                       while (midi_requests.read (&request, 1) == 1) {
-
-                               switch (request->type) {
-                               case MIDIRequest::PortChange:
-                                       /* restart poll with new ports */
-                                       DEBUG_TRACE (DEBUG::MidiIO, "rebind\n");
-                                       restart = true;
-                                       break;
-
-                               case MIDIRequest::Quit:
-                                       delete request;
-                                       DEBUG_TRACE (DEBUG::MidiIO, "thread quit\n");
-                                       pthread_exit_pbd (0);
-                                       /*NOTREACHED*/
-                                       break;
-
-                               default:
-                                       break;
-                               }
-
-
-                               delete request;
-                       }
-
-               }
-
-               if (restart) {
-                       DEBUG_TRACE (DEBUG::MidiIO, "ports changed, restart poll\n");
-                       restart = false;
-                       continue;
-               }
-
-               /* now read the rest of the ports */
-
-               for (int p = 1; p < nfds; ++p) {
-
-#ifndef NDEBUG
-                       DEBUG_STR_SET(foo, "port #%1 revents = ");
-                       DEBUG_STR(foo) << hex << pfd[p].revents << dec << endl;
-                       DEBUG_TRACE (DEBUG::MidiIO, string_compose (DEBUG_STR(foo).str(), p));
-#endif
-
-                       if ((pfd[p].revents & ~POLLIN)) {
-                               // error << string_compose(_("Transport: error polling MIDI port %1 (revents =%2%3%4"), p, &hex, pfd[p].revents, &dec) << endmsg;
-                               break;
-                       }
-
-                       if (pfd[p].revents & POLLIN) {
-                               DEBUG_TRACE (DEBUG::MidiIO, string_compose ("MIDI fd # %1 has data ready @ %2\n", p, now));
-                               fds_ready++;
-                               ports[p]->parse (now);
-                       }
-               }
-
-               /* timeout driven */
-
-               if (fds_ready < 2 && timeout != -1) {
-
-                       DEBUG_TRACE (DEBUG::MidiIO, "Check timeouts\n");
-                       for (MidiTimeoutList::iterator i = midi_timeouts.begin(); i != midi_timeouts.end(); ) {
-
-                               MidiTimeoutList::iterator tmp;
-                               tmp = i;
-                               ++tmp;
-
-                               if (!(*i)()) {
-                                       midi_timeouts.erase (i);
-                               }
-
-                               i = tmp;
-                       }
-               }
-       }
-}
 
index 078ef260c631b4d1168f0feb1384ff8bc6d8a1bf..882694cccf5ead23a5181b93bbb76a8e3923236f 100644 (file)
@@ -51,10 +51,6 @@ using namespace std;
 void
 Session::process (nframes_t nframes)
 {
-       // This is no more the appropriate place to call cycle
-       // start. cycle_start needs to be called at the Route::roll()
-       // where the signals which we want to mixdown have been calculated.
-       //
        MIDI::Manager::instance()->cycle_start(nframes);
 
        _silent = false;
index 3aaa3dbf22c2c85a9d5caa048ae603fa52596758..f1bfa5ea92b108482e44a04b1631f28a41383695 100644 (file)
@@ -219,7 +219,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        first_file_data_format_reset = true;
        first_file_header_format_reset = true;
        post_export_sync = false;
-       //midi_thread = (pthread_t) 0;
+       midi_control_ui = 0;
 
        AudioDiskstream::allocate_working_buffers();
 
index 7c8943a4f1e6f6499cd159c1d44ed3fafca782f1..a44a372726f79230b148e5e3ca1fcd8cd18f7f9e 100644 (file)
@@ -58,7 +58,6 @@ std::list<boost::weak_ptr<AudioSource> > SourceFactory::files_with_peaks;
 static void
 peak_thread_work ()
 {
-       PBD::notify_gui_about_thread_creation (pthread_self(), string ("peakbuilder-") + to_string (pthread_self(), std::dec));
        SessionEvent::create_per_thread_pool (X_("PeakFile Builder "), 64);
 
        while (true) {
index b01260a30ba01d0cba6e0595b57ee355bfdc6bfa..3e5f763461f243acbe783f965244151a1eb224b5 100644 (file)
@@ -124,6 +124,7 @@ libardour_sources = [
        'midi_state_tracker.cc',
        'midi_stretch.cc',
        'midi_track.cc',
+       'midi_ui.cc',
        'mix.cc',
        'mtc_slave.cc',
        'mtdm.cc',
index 944019d4c350321d78af30c026eb9039678d1ef8..fb251e8078a7e87afcab4597ad568b5f0cf8ae3f 100644 (file)
@@ -47,7 +47,6 @@ using namespace Glib;
 using namespace PBD;
 using std::map;
 
-pthread_t UI::gui_thread;
 UI       *UI::theGtkUI = 0;
 
 BaseUI::RequestType Gtkmm2ext::ErrorMessage = BaseUI::new_request_type();
@@ -58,11 +57,10 @@ 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 */
-
+#include "pbd/abstract_ui.cc"  /* instantiate the template */
 
 UI::UI (string namestr, int *argc, char ***argv)
-       : AbstractUI<UIRequest> (namestr, true)
+       : AbstractUI<UIRequest> (namestr)
 {
        theMain = new Main (argc, argv);
 #ifndef GTK_NEW_TOOLTIP_API
@@ -73,18 +71,20 @@ UI::UI (string namestr, int *argc, char ***argv)
 
        if (!theGtkUI) {
                theGtkUI = this;
-               gui_thread = pthread_self ();
        } else {
                fatal << "duplicate UI requested" << endmsg;
                /* NOTREACHED */
        }
 
-       /* add the pipe to the select/poll loop that GDK does */
+       /* the GUI event loop runs in the main thread of the app,
+          which is assumed to have called this.
+       */
+
+       run_loop_thread = Thread::self();
 
-       gdk_input_add (signal_pipe[0],
-                      GDK_INPUT_READ,
-                      UI::signal_pipe_callback,
-                      this);
+       /* attach our request source to the default main context */
+
+       request_channel.ios()->attach (MainContext::get_default());
 
        errors = new TextViewer (800,600);
        errors->text().set_editable (false);
@@ -100,8 +100,6 @@ UI::UI (string namestr, int *argc, char ***argv)
        errors->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors));
        errors->set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
 
-       register_thread (pthread_self(), X_("GUI"));
-
        //load_rcfile (rcfile);
 }
 
@@ -113,7 +111,7 @@ UI::~UI ()
 bool
 UI::caller_is_ui_thread ()
 {
-       return pthread_equal (gui_thread, pthread_self());
+       return Thread::self() == run_loop_thread;
 }
 
 int
@@ -122,7 +120,9 @@ UI::load_rcfile (string path, bool themechange)
        /* Yes, pointers to Glib::RefPtr.  If these are not kept around,
         * a segfault somewhere deep in the wonderfully robust glib will result.
         * This does not occur if wiget.get_style is used instead of rc.get_style below,
-        * except that doesn't actually work... */
+        * except that doesn't actually work... 
+        */
+
        static Glib::RefPtr<Style>* fatal_style   = 0;
        static Glib::RefPtr<Style>* error_style   = 0;
        static Glib::RefPtr<Style>* warning_style = 0;
@@ -250,14 +250,6 @@ UI::running ()
        return _active;
 }
 
-void
-UI::kill ()
-{
-       if (_active) {
-               pthread_kill (gui_thread, SIGKILL);
-       }
-}
-
 void
 UI::quit ()
 {
@@ -348,18 +340,6 @@ UI::idle_add (int (*func)(void *), void *arg)
 
 /* END abstract_ui interfaces */
 
-void
-UI::signal_pipe_callback (void *arg, int fd, GdkInputCondition /*cond*/)
-{
-       char buf[256];
-
-       /* flush (nonblocking) pipe */
-
-       while (read (fd, buf, 256) > 0) {}
-
-       ((UI *) arg)->handle_ui_requests ();
-}
-
 void
 UI::do_request (UIRequest* req)
 {
@@ -375,7 +355,7 @@ UI::do_request (UIRequest* req)
 
        } else if (req->type == CallSlot) {
 
-               req->slot ();
+               req->the_slot ();
 
        } else if (req->type == TouchDisplay) {
 
@@ -550,10 +530,9 @@ UI::handle_fatal (const char *message)
 
        win.set_default_size (400, 100);
 
-       string title;
-       title = name();
+       WindowTitle title(Glib::get_application_name());
        title += ": Fatal Error";
-       win.set_title (title);
+       win.set_title (title.get_string());
 
        win.set_position (WIN_POS_MOUSE);
        win.set_border_width (12);
index 27eb39502430791d5da26523f8cedd17213547af..3126475f6ab4487ddc63e85446a26f78d97ec5fe 100644 (file)
@@ -26,6 +26,9 @@
 #include <stdint.h>
 #include <setjmp.h>
 #include <pthread.h>
+
+#include <glibmm/thread.h>
+
 #include <gtkmm/widget.h>
 #include <gtkmm/style.h>
 #ifndef GTK_NEW_TOOLTIP_API
@@ -72,7 +75,6 @@ struct UIRequest : public BaseUI::BaseRequestObject {
     Transmitter::Channel chn;
     void *arg;
     const char *msg2;
-    sigc::slot<void> slot;
     
     ~UIRequest () { 
            if (type == ErrorMessage && msg) {
@@ -80,7 +82,7 @@ struct UIRequest : public BaseUI::BaseRequestObject {
                    free ((char *)msg);
            }
     }
- };
+};
 
 class UI : public Receiver, public AbstractUI<UIRequest>
 {
@@ -98,13 +100,12 @@ class UI : public Receiver, public AbstractUI<UIRequest>
 
        bool caller_is_ui_thread ();
 
-       static pthread_t thread_id() { return gui_thread; }
+       static Glib::Thread* thread_id() { return gui_thread; }
 
        /* Gtk-UI specific interfaces */
 
        bool running ();
        void quit    ();
-       void kill    ();
        int  load_rcfile (std::string, bool themechange = false);
        void run (Receiver &old_receiver);
 
@@ -136,7 +137,7 @@ class UI : public Receiver, public AbstractUI<UIRequest>
 
        static bool just_hide_it (GdkEventAny *, Gtk::Window *);
 
-       static pthread_t the_gui_thread() { return gui_thread; }
+       static Glib::Thread* the_gui_thread() { return gui_thread; }
 
   protected:
        virtual void handle_fatal (const char *);
@@ -146,7 +147,7 @@ class UI : public Receiver, public AbstractUI<UIRequest>
 
   private:
        static UI *theGtkUI;
-       static pthread_t gui_thread;
+       static Glib::Thread* gui_thread;
        bool _active;
        Gtk::Main *theMain;
 #ifndef GTK_NEW_TOOLTIP_API
index c86402e54e7b5841a488071da4c3fe8fe9683f05..1ba8ddf6f50bd67d69682f5cbd0ceb43d503485a 100644 (file)
@@ -42,7 +42,7 @@ class StateButton
        bool _is_realized;
 
        virtual std::string get_widget_name() const = 0;
-       virtual void set_widget_name (std::string) = 0;
+       virtual void set_widget_name (const std::string&) = 0;
        virtual int get_widget_state() = 0;
 };
 
@@ -59,7 +59,7 @@ class StatefulToggleButton : public StateButton, public Gtk::ToggleButton
        void on_toggled ();
 
        std::string get_widget_name() const { return get_name(); }
-       void set_widget_name (std::string name) { set_name (name); get_child()->set_name (name); }
+       void set_widget_name (const std::string& name);
        int get_widget_state() { return get_state(); }
 };
 
@@ -74,7 +74,7 @@ class StatefulButton : public StateButton, public Gtk::Button
        void on_realize ();
 
        std::string get_widget_name() const { return get_name(); }
-       void set_widget_name (std::string name) { set_name (name); get_child()->set_name (name); }
+       void set_widget_name (const std::string& name);
        int get_widget_state() { return get_state(); }
 };
 
index 580e0deb1c6e080655eb8dcab8b12061e7ae3a8f..66cc2f192aa045d6153bc1693627c4adafa0cce7 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <gtkmm/main.h>
 
+#include "pbd/stacktrace.h"
+
 #include <gtkmm2ext/stateful_button.h>
 
 using namespace Gtk;
@@ -99,3 +101,31 @@ StatefulToggleButton::on_toggled ()
                }
        }
 }
+
+void
+StatefulToggleButton::set_widget_name (const std::string& name)
+{
+       set_name (name); 
+       Widget* w = get_child();
+
+       if (w) {
+               w->set_name (name); 
+       } else {
+               cerr << "Statefull TOggle button - no child\n";
+               PBD::stacktrace (cerr, 20);
+       }
+}
+
+void
+StatefulButton::set_widget_name (const std::string& name)
+{
+       set_name (name); 
+       Widget* w = get_child();
+
+       if (w) {
+               w->set_name (name); 
+       } else {
+               cerr << "Stateful button - no child\n";
+               PBD::stacktrace (cerr, 20);
+       }
+}
index e950da294909600724b9cca160f9fc44f9e47d90..4349ea45f49f43d1938fe969ad40df2454cd8d3e 100644 (file)
@@ -122,22 +122,29 @@ void CoreMidi_MidiPort::read_proc (const MIDIPacketList *pktlist, void *refCon,
 
     if (driver->firstrecv) {
            driver->firstrecv = false;
-           PBD::notify_gui_about_thread_creation (pthread_self(), "COREMIDI");
+           PBD::notify_gui_about_thread_creation ("gui", pthread_self(), "COREMIDI", 256);
     }
 
     for (unsigned int i = 0; i < pktlist->numPackets; ++i) {
-    
-        driver->bytes_read += packet->length;
-               
+           
+           driver->bytes_read += packet->length;
+           
            if (driver->input_parser) {
-                       driver->input_parser->raw_preparse (*driver->input_parser, packet->data, packet->length);
-                       for (int i = 0; i < packet->length; i++) {
-                               driver->input_parser->scanner (packet->data[i]);
-                       }       
-                       driver->input_parser->raw_postparse (*driver->input_parser, packet->data, packet->length);
-               }
-                 
-        packet = MIDIPacketNext(packet);
+                   //driver->input_parser->raw_preparse (*driver->input_parser, packet->data, packet->length);
+                   
+                   /* XXX This is technically the wrong timebase, since it is based on
+                      host time.
+                   */
+                   driver->input_parser->set_timestamp (packet->timestamp);
+                   
+                   for (int i = 0; i < packet->length; i++) {
+                           driver->input_parser->scanner (packet->data[i]);
+                   }   
+                   
+                   //driver->input_parser->raw_postparse (*driver->input_parser, packet->data, packet->length);
+           }
+           
+           packet = MIDIPacketNext(packet);
     }
 }
 
index ada72f1be62c6e2e8b3ddaea8d560585ea397849..9b96155b88c2f662f3dcf8b593648796494b6f01 100644 (file)
 #include <fcntl.h>
 #include <cerrno>
 #include <cassert>
+#include <cstring>
 #include <cstdlib>
 
 #include "pbd/error.h"
+#include "pbd/compose.h"
 
 #include "midi++/types.h"
 #include "midi++/jack.h"
@@ -39,11 +41,10 @@ JACK_MidiPort::JACK_MidiPort(const XMLNode& node, jack_client_t* jack_client)
        , _jack_input_port(NULL)
        , _jack_output_port(NULL)
        , _last_read_index(0)
-       , non_process_thread_fifo (512)
+       , output_fifo (512)
+       , input_fifo (1024)
 {
-       int err = create_ports (node);
-
-       if (!err) {
+       if (!create_ports (node)) {
                _ok = true;
        }
 }
@@ -82,19 +83,16 @@ JACK_MidiPort::cycle_start (nframes_t nframes)
                const nframes_t event_count = jack_midi_get_event_count(jack_buffer);
 
                jack_midi_event_t ev;
-               nframes_t cycle_start_frame = jack_last_frame_time (_jack_client);
-
-               for (nframes_t i=0; i < event_count; ++i) {
+               timestamp_t cycle_start_frame = jack_last_frame_time (_jack_client);
 
+               for (nframes_t i = 0; i < event_count; ++i) {
                        jack_midi_event_get (&ev, jack_buffer, i);
-
-                       if (input_parser) {
-                               for (size_t i = 0; i < ev.size; i++) {
-                                       input_parser->set_timestamp (cycle_start_frame + ev.time);
-                                       input_parser->scanner (ev.buffer[i]);
-                               }       
-                       }
+                       input_fifo.write (cycle_start_frame + ev.time, (Evoral::EventType) 0, ev.size, ev.buffer);
                }       
+               
+               if (event_count) {
+                       xthread.wakeup ();
+               }
        }
 }
 
@@ -104,6 +102,8 @@ JACK_MidiPort::cycle_end ()
        if (_jack_output_port != 0) {
                flush (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle));
        }
+
+       Port::cycle_end();
 }
 
 int
@@ -113,10 +113,10 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
 
        if (!is_process_thread()) {
 
-               Glib::Mutex::Lock lm (non_process_thread_fifo_lock);
+               Glib::Mutex::Lock lm (output_fifo_lock);
                RingBuffer< Evoral::Event<double> >::rw_vector vec;
                
-               non_process_thread_fifo.get_write_vector (&vec);
+               output_fifo.get_write_vector (&vec);
 
                if (vec.len[0] + vec.len[1] < 1) {
                        error << "no space in FIFO for non-process thread MIDI write" << endmsg;
@@ -129,10 +129,10 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
                        vec.buf[1]->set (msg, msglen, timestamp);
                }
 
-               non_process_thread_fifo.increment_write_idx (1);
+               output_fifo.increment_write_idx (1);
                
                ret = msglen;
-                               
+
        } else {
 
                assert(_jack_output_port);
@@ -164,11 +164,13 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
        }
 
        if (ret > 0 && output_parser) {
-               output_parser->raw_preparse (*output_parser, msg, ret);
+               // ardour doesn't care about this and neither should your app, probably
+               // output_parser->raw_preparse (*output_parser, msg, ret);
                for (int i = 0; i < ret; i++) {
                        output_parser->scanner (msg[i]);
                }
-               output_parser->raw_postparse (*output_parser, msg, ret);
+               // ardour doesn't care about this and neither should your app, probably
+               // output_parser->raw_postparse (*output_parser, msg, ret);
        }       
 
        return ret;
@@ -180,7 +182,7 @@ JACK_MidiPort::flush (void* jack_port_buffer)
        RingBuffer< Evoral::Event<double> >::rw_vector vec;
        size_t written;
 
-       non_process_thread_fifo.get_read_vector (&vec);
+       output_fifo.get_read_vector (&vec);
 
        if (vec.len[0] + vec.len[1]) {
                // cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n";
@@ -205,15 +207,28 @@ JACK_MidiPort::flush (void* jack_port_buffer)
        }
        
        if ((written = vec.len[0] + vec.len[1]) != 0) {
-               non_process_thread_fifo.increment_read_idx (written);
+               output_fifo.increment_read_idx (written);
        }
 }
 
 int
-JACK_MidiPort::read(byte * buf, size_t bufsize)
+JACK_MidiPort::read (byte * buf, size_t bufsize)
 {
-       cerr << "This program is improperly written. JACK_MidiPort::read() should never be called\n";
-       abort ();
+       timestamp_t time;
+       Evoral::EventType type;
+       uint32_t size;
+       byte buffer[input_fifo.capacity()];
+
+       while (input_fifo.read (&time, &type, &size, buffer)) {
+               if (input_parser) {
+                       input_parser->set_timestamp (time);
+                       for (uint32_t i = 0; i < size; ++i) {
+                               input_parser->scanner (buffer[i]);
+                       }
+               }
+       }
+
+       return 0;
 }
 
 int
index b6bbd40b0b41404fe27d2a22b19cd8dfbd594b8a..ffc8f4e1bc88173a82afe13be75991607710c31d 100644 (file)
@@ -115,6 +115,8 @@ Manager::add_port (const XMLNode& node)
                outputPort = port;
        }
 
+       PortsChanged (); /* EMIT SIGNAL */
+
        return port;
 }
 
@@ -124,11 +126,16 @@ Manager::remove_port (Port* port)
        if (inputPort == port) {
                inputPort = 0;
        }
+
        if (outputPort == port) {
                outputPort = 0;
        }
+
        _ports.remove (port);
        delete port;
+
+       PortsChanged (); /* EMIT SIGNAL */
+
        return 0;
 }
 
index a449a8c5542fd7fcbf50fb7335720a5be1bd650f..18a6f3df686ef9a5795645a178ecd9e0bd2c3d3d 100644 (file)
 
 #include <glibmm/thread.h>
 
-#include "pbd/ringbuffer.h"
 #include <jack/jack.h>
 #include <jack/midiport.h>
+
+#include "pbd/ringbuffer.h"
+#include "pbd/crossthread.h"
+#include "evoral/EventRingBuffer.hpp"
+
 #include "midi++/port.h"
 #include "midi++/event.h"
 
@@ -49,16 +53,16 @@ public:
        int write(byte *msg, size_t msglen, timestamp_t timestamp);
        int read(byte *buf, size_t max);
 
-       /* No select(2)/poll(2)-based I/O */
-       virtual int selectable() const { return -1; }
+       int selectable() const { return xthread.selectable(); }
+       bool must_drain_selectable() const { return true; }
        
-       virtual void cycle_start(nframes_t nframes);
-       virtual void cycle_end();
+       void cycle_start(nframes_t nframes);
+       void cycle_end();
 
        static std::string typestring;
 
-       virtual XMLNode& get_state () const;
-       virtual void set_state (const XMLNode&);
+       XMLNode& get_state () const;
+       void set_state (const XMLNode&);
 
        static void set_process_thread (pthread_t);
        static pthread_t get_process_thread () { return _process_thread; }
@@ -79,13 +83,16 @@ private:
        jack_port_t*   _jack_output_port;
        nframes_t      _last_read_index;
        timestamp_t    _last_write_timestamp;
+       CrossThreadChannel xthread;
 
        void flush (void* jack_port_buffer);
 
        static pthread_t _process_thread;
 
-       RingBuffer< Evoral::Event<double> > non_process_thread_fifo;
-       Glib::Mutex non_process_thread_fifo_lock;
+       RingBuffer< Evoral::Event<double> > output_fifo;
+       Evoral::EventRingBuffer<timestamp_t> input_fifo;
+
+       Glib::Mutex output_fifo_lock;
 };
 
 
index 0698e969c0829b63a803abdbf7c6055267ec1025..8c665d0086f1221f59a5b55ff29ba72f18def094 100644 (file)
@@ -83,6 +83,8 @@ class Manager {
 
        int get_known_ports (std::vector<PortSet>&);
 
+       sigc::signal<void> PortsChanged;
+
   private:
        /* This is a SINGLETON pattern */
        
index 1bab068f853ce94a3cba4a5197664059ac6e3271..23a9a01200779001491555ae9d39fd10b25995c9 100644 (file)
@@ -112,6 +112,7 @@ class Port : public sigc::trackable {
         * @return File descriptor, or -1 if not selectable. 
         */
        virtual int selectable() const = 0;
+       virtual bool must_drain_selectable() const { return false; }
 
        static void gtk_read_callback (void *ptr, int fd, int cond);
        static void write_callback (byte *msg, unsigned int len, void *);
index 015951f118cf825532cf943ade511d97e0c9266b..259a51d9549b806e16da2250c488c778e05189fd 100644 (file)
 
 using namespace std;
 using namespace PBD;
+using namespace Glib;
        
-uint32_t BaseUI::rt_bit = 1;
+uint64_t BaseUI::rt_bit = 1;
 BaseUI::RequestType BaseUI::CallSlot = BaseUI::new_request_type();
 
-BaseUI::BaseUI (string str, bool with_signal_pipe)
-       : _name (str)
+BaseUI::BaseUI (const string& str)
+       : run_loop_thread (0)
+       , _name (str)
 {
-       /* odd pseudo-singleton semantics */
-
        base_ui_instance = this;
 
-       signal_pipe[0] = -1;
-       signal_pipe[1] = -1;
+       request_channel.ios()->connect (sigc::mem_fun (*this, &BaseUI::request_handler));
 
-       if (with_signal_pipe) {
-               if (setup_signal_pipe ()) {
-                       throw failed_constructor ();
-               }
-       }
+       /* derived class must set _ok */
 }
 
 BaseUI::~BaseUI()
 {
-       if (signal_pipe[0] >= 0) {
-               close (signal_pipe[0]);
-       } 
-
-       if (signal_pipe[1] >= 0) {
-               close (signal_pipe[1]);
-       } 
 }
 
 BaseUI::RequestType
@@ -78,32 +66,53 @@ BaseUI::new_request_type ()
        return rt;
 }
 
-int
-BaseUI::setup_signal_pipe ()
+void
+BaseUI::main_thread ()
+{
+       thread_init ();
+       _main_loop->run ();
+}
+
+void
+BaseUI::run ()
 {
-       /* setup the pipe that other threads send us notifications/requests
-          through.
+       /* to be called by UI's that need/want their own distinct, self-created event loop thread.
+          Derived classes should have set up a handler for IO on request_channel.ios()
        */
 
-       if (pipe (signal_pipe)) {
-               error << string_compose (_("%1-UI: cannot create error signal pipe (%2)"), _name, ::strerror (errno))
-                     << endmsg;
+       _main_loop = MainLoop::create (MainContext::create());
+       request_channel.ios()->attach (_main_loop->get_context());
+       run_loop_thread = Thread::create (mem_fun (*this, &BaseUI::main_thread), true);
+}
 
-               return -1;
-       }
+void
+BaseUI::quit ()
+{
+       _main_loop->quit ();
+       run_loop_thread->join ();
+}
+
+bool
+BaseUI::request_handler (Glib::IOCondition ioc)
+{
+       /* check the transport request pipe */
 
-       if (fcntl (signal_pipe[0], F_SETFL, O_NONBLOCK)) {
-               error << string_compose (_("%1-UI: cannot set O_NONBLOCK on signal read pipe (%2)"), _name, ::strerror (errno))
-                     << endmsg;
-               return -1;
+       if (ioc & ~IO_IN) {
+               _main_loop->quit ();
        }
 
-       if (fcntl (signal_pipe[1], F_SETFL, O_NONBLOCK)) {
-               error << string_compose (_("%1-UI: cannot set O_NONBLOCK on signal write pipe (%2)"), _name, ::strerror (errno))
-                     << endmsg;
-               return -1;
+       if (ioc & IO_IN) {
+               request_channel.drain ();
+               
+               /* there may been an error. we'd rather handle requests first,
+                  and then get IO_HUP or IO_ERR on the next loop.
+               */
+
+               /* handle requests */
+
+               handle_ui_requests ();
        }
 
-       return 0;
+       return true;
 }
-
+       
diff --git a/libs/pbd/crossthread.cc b/libs/pbd/crossthread.cc
new file mode 100644 (file)
index 0000000..1465505
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+    Copyright (C) 2009 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.
+
+*/
+
+#include <cstdlib>
+#include <cerrno>
+#include <cstring>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "pbd/error.h"
+#include "pbd/crossthread.h"
+
+using namespace std;
+using namespace PBD;
+using namespace Glib;
+
+CrossThreadChannel::CrossThreadChannel ()
+{
+       fds[0] = -1;
+       fds[1] = -1;
+
+       if (pipe (fds)) {
+               error << "cannot create x-thread pipe for read (%2)" << ::strerror (errno) << endmsg;
+               return;
+       }
+
+       if (fcntl (fds[0], F_SETFL, O_NONBLOCK)) {
+               error << "cannot set non-blocking mode for x-thread pipe (read) (" << ::strerror (errno) << ')' << endmsg;
+               return;
+       }
+
+       if (fcntl (fds[1], F_SETFL, O_NONBLOCK)) {
+               error << "cannot set non-blocking mode for x-thread pipe (write) (%2)" << ::strerror (errno) << ')' << endmsg;
+               return;
+       }
+
+}
+
+CrossThreadChannel::~CrossThreadChannel ()
+{
+       _ios->destroy ();
+
+       if (fds[0] >= 0) {
+               close (fds[0]);
+               fds[0] = -1;
+       } 
+
+       if (fds[1] >= 0) {
+               close (fds[1]);
+               fds[1] = -1;
+       } 
+}
+
+void
+CrossThreadChannel::wakeup ()
+{
+       char c = 0;
+       ::write (fds[1], &c, 1);
+}
+
+RefPtr<IOSource>
+CrossThreadChannel::ios () 
+{
+       if (!_ios) {
+               _ios = IOSource::create (fds[0], IOCondition(IO_IN|IO_PRI|IO_ERR|IO_HUP|IO_NVAL));
+       }
+       return _ios;
+}
+       
+void
+CrossThreadChannel::drain ()
+{
+       drain (fds[0]);
+}
+
+void
+CrossThreadChannel::drain (int fd)
+{
+       /* drain selectable fd */
+       char buf[64];
+       while (::read (fd, buf, sizeof (buf)) > 0);
+}
index 25c198774c22657c01d22da0814eca9a09817eeb..07f6d3a4b1a6e5574d5a7a9e62249b55e13cbbd0 100644 (file)
@@ -1,5 +1,6 @@
 #include <unistd.h>
 
+#include "pbd/stacktrace.h"
 #include "pbd/abstract_ui.h"
 #include "pbd/pthread_utils.h"
 #include "pbd/failed_constructor.h"
 using namespace std;
 
 template <typename RequestObject>
-AbstractUI<RequestObject>::AbstractUI (string name, bool with_signal_pipes)
-       : BaseUI (name, with_signal_pipes)
+AbstractUI<RequestObject>::AbstractUI (const string& name)
+       : BaseUI (name)
 {
-       if (pthread_key_create (&thread_request_buffer_key, 0)) {
-               cerr << _("cannot create thread request buffer key") << endl;
-               throw failed_constructor();
-       }
-
-       PBD::ThreadCreatedWithRequestSize.connect (mem_fun (*this, &AbstractUI<RequestObject>::register_thread_with_request_count));
+       PBD::ThreadCreatedWithRequestSize.connect (mem_fun (*this, &AbstractUI<RequestObject>::register_thread));
 }
 
 template <typename RequestObject> void
-AbstractUI<RequestObject>::register_thread (pthread_t thread_id, string name)
+AbstractUI<RequestObject>::register_thread (string target_gui, pthread_t thread_id, string /*thread_name*/, uint32_t num_requests)
 {
-       register_thread_with_request_count (thread_id, name, 256);
-}
+       if (target_gui != name()) {
+               return;
+       }
 
-template <typename RequestObject> void
-AbstractUI<RequestObject>::register_thread_with_request_count (pthread_t thread_id, string /*thread_name*/, uint32_t num_requests)
-{
        RequestBuffer* b = new RequestBuffer (num_requests);
 
        {
-        Glib::Mutex::Lock lm (request_buffer_map_lock);
+               Glib::Mutex::Lock lm (request_buffer_map_lock);
                request_buffers[thread_id] = b;
        }
 
-       pthread_setspecific (thread_request_buffer_key, b);
+       per_thread_request_buffer.set (b);
 }
 
 template <typename RequestObject> RequestObject*
 AbstractUI<RequestObject>::get_request (RequestType rt)
 {
-       RequestBuffer* rbuf = static_cast<RequestBuffer*>(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 ("no %1-UI request buffer found for thread %2", name(), pthread_name())
-                    << endl;
-               abort ();
-       }
-       
+       RequestBuffer* rbuf = per_thread_request_buffer.get ();
        RequestBufferVector vec;
-       vec.buf[0] = 0;
-       vec.buf[1] = 0;
-       
-       rbuf->get_write_vector (&vec);
 
-       if (vec.len[0] == 0) {
-               if (vec.len[1] == 0) {
-                       cerr << string_compose ("no space in %1-UI request buffer for thread %2", name(), pthread_name())
-                            << endl;
+       if (rbuf != 0) {
+               /* we have a per-thread FIFO, use it */
+
+               rbuf->get_write_vector (&vec);
+
+               if (vec.len[0] == 0) {
                        return 0;
-               } else {
-                       vec.buf[1]->type = rt;
-                       return vec.buf[1];
                }
-       } else {
+
                vec.buf[0]->type = rt;
                return vec.buf[0];
        }
+
+       RequestObject* req = new RequestObject;
+       req->type = rt;
+       return req;
 }
 
 template <typename RequestObject> void
 AbstractUI<RequestObject>::handle_ui_requests ()
 {
        RequestBufferMapIterator i;
+       RequestBufferVector vec;
+
+       /* per-thread buffers first */
 
        request_buffer_map_lock.lock ();
 
        for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
 
-               RequestBufferVector vec;
-
                while (true) {
 
                        /* we must process requests 1 by 1 because
@@ -110,6 +95,22 @@ AbstractUI<RequestObject>::handle_ui_requests ()
        }
 
        request_buffer_map_lock.unlock ();
+
+       /* and now, the generic request buffer. same rules as above apply */
+
+       Glib::Mutex::Lock lm (request_list_lock);
+
+       while (!request_list.empty()) {
+               RequestObject* req = request_list.front ();
+               request_list.pop_front ();
+               lm.release ();
+
+               do_request (req);
+
+               delete req;
+
+               lm.acquire();
+       }
 }
 
 template <typename RequestObject> void
@@ -118,31 +119,41 @@ AbstractUI<RequestObject>::send_request (RequestObject *req)
        if (base_instance() == 0) {
                return; /* XXX is this the right thing to do ? */
        }
-       
-       if (caller_is_ui_thread()) {
-               // cerr << "GUI thread sent request " << req << " type = " << req->type << endl;
+
+       if (caller_is_self ()) {
                do_request (req);
        } else {        
-               RequestBuffer* rbuf = static_cast<RequestBuffer*> (pthread_getspecific (thread_request_buffer_key));
+               RequestBuffer* rbuf = per_thread_request_buffer.get ();
 
-               if (rbuf == 0) {
-                       /* can't use the error system to report this, because this
-                          thread isn't registered!
+               if (rbuf != 0) {
+                       rbuf->increment_write_ptr (1);
+               } else {
+                       /* no per-thread buffer, so just use a list with a lock so that it remains
+                          single-reader/single-writer semantics
                        */
-                       cerr << _("programming error: ")
-                            << string_compose ("AbstractUI::send_request() called from %1 (%2), but no request buffer exists for that thread", name(), pthread_name())
-                            << endl;
-                       abort ();
+                       Glib::Mutex::Lock lm (request_list_lock);
+                       request_list.push_back (req);
                }
-               
-               // cerr << "thread " << pthread_self() << " sent request " << req << " type = " << req->type << endl;
 
-               rbuf->increment_write_ptr (1);
-
-               if (signal_pipe[1] >= 0) {
-                       const char c = 0;
-                       write (signal_pipe[1], &c, 1);
-               }
+               request_channel.wakeup ();
        }
 }
 
+template<typename RequestObject> void
+AbstractUI<RequestObject>::call_slot (sigc::slot<void> elSlot)
+{
+       if (caller_is_self()) {
+               elSlot ();
+               return;
+       }
+
+       RequestObject *req = get_request (BaseUI::CallSlot);
+       
+       if (req == 0) {
+               return;
+       }
+
+       req->the_slot = elSlot;
+       send_request (req);
+}      
+
index adb9aabf0e5de976f0a4e33ab9fe06f4c0d9dab2..daa1b83e3ce984287c486d870c3e3e6e4f1293e8 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 1998-99 Paul Barton-Davis 
+    Copyright (C) 1998-2009 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
 
 class Touchable;
 
-template <class RequestObject>
+template<typename RequestObject>
 class AbstractUI : public BaseUI
 {
   public:
-       AbstractUI (std::string name, bool with_signal_pipe);
+       AbstractUI (const std::string& name);
        virtual ~AbstractUI() {}
 
-       virtual bool caller_is_ui_thread() = 0;
-
-       void call_slot (sigc::slot<void> el_slot) {
-               RequestObject *req = get_request (BaseUI::CallSlot);
-               
-               if (req == 0) {
-                       return;
-               }
-               
-               req->slot = el_slot;
-               send_request (req);
-       }       
-
-       void register_thread (pthread_t, std::string);
-       void register_thread_with_request_count (pthread_t, std::string, uint32_t num_requests);
+       void register_thread (std::string, pthread_t, std::string, uint32_t num_requests);
+       void call_slot (sigc::slot<void> el_slot);
 
   protected:
        typedef RingBufferNPT<RequestObject> RequestBuffer;
        typedef typename RequestBuffer::rw_vector RequestBufferVector;
        typedef typename std::map<pthread_t,RequestBuffer*>::iterator RequestBufferMapIterator;
-
-    Glib::Mutex request_buffer_map_lock;
        typedef std::map<pthread_t,RequestBuffer*> RequestBufferMap;
+
+       Glib::Mutex request_buffer_map_lock;
        RequestBufferMap request_buffers;
-       pthread_key_t thread_request_buffer_key;
+       Glib::Private<RequestBuffer> per_thread_request_buffer;
+
+       Glib::Mutex               request_list_lock;
+       std::list<RequestObject*> request_list;
+       
        RequestObject* get_request (RequestType);
        void handle_ui_requests ();
        void send_request (RequestObject *);
index 0928512841231afca93c4aaf22dafdc3d6b419cc..614873e5d1110fc9dff3b4b32856257cbd5ee589 100644 (file)
 #include <sigc++/slot.h>
 #include <sigc++/trackable.h>
 
+#include <glibmm/thread.h>
+#include <glibmm/main.h>
+
+#include "pbd/crossthread.h"
+
 class BaseUI : virtual public sigc::trackable {
   public:
-       BaseUI (std::string name, bool with_signal_pipes);
+       BaseUI (const std::string& name);
        virtual ~BaseUI();
 
        BaseUI* base_instance() { return base_ui_instance; }
 
+       Glib::RefPtr<Glib::MainLoop> main_loop() const { return _main_loop; }
+       Glib::Thread* event_loop_thread() const { return run_loop_thread; }
+       bool caller_is_self () const { return Glib::Thread::self() == run_loop_thread; }
+
        std::string name() const { return _name; }
 
        bool ok() const { return _ok; }
@@ -49,17 +58,31 @@ class BaseUI : virtual public sigc::trackable {
        static RequestType new_request_type();
        static RequestType CallSlot;
 
+       void run ();
+       void quit ();
+
+       virtual void call_slot (sigc::slot<void> theSlot) = 0;
+
   protected:
-       int signal_pipe[2];
+       CrossThreadChannel request_channel;
        bool _ok; 
 
+       Glib::RefPtr<Glib::MainLoop> _main_loop;
+       Glib::Thread*                 run_loop_thread;
+
+       virtual void thread_init () {};
+       bool request_handler (Glib::IOCondition);
+
+       virtual void handle_ui_requests () = 0;
+
   private:
        std::string _name; 
        BaseUI* base_ui_instance;
+       
+       static uint64_t rt_bit;
 
-       static uint32_t rt_bit;
-
-       int setup_signal_pipe ();
+       int setup_request_pipe ();
+       void main_thread ();
 };
 
 #endif /* __pbd_base_ui_h__ */
index c63c863f385d41e698dc0d4f07853930c2c63b48..f2fb4aa46908a690e3531e0773a208f59e102ff0 100644 (file)
 #ifndef __pbd__crossthread_h__
 #define __pbd__crossthread_h__
 
-#include "pbd/abstract_ui.h"
-#include <sigc++/sigc++.h>
-#include <pthread.h>
-
-template<class RequestType> 
-void 
-call_slot_from_thread_or_dispatch_it (pthread_t thread_id, AbstractUI<RequestType>& ui, sigc::slot<void> theSlot)
-{
-       /* when called, this function will determine whether the calling thread
-          is the same as thread specified by the first argument. if it is,
-          the we execute the slot. if not, we ask the interface given by the second
-          argument to call the slot.
-       */
-
-       if (pthread_self() == thread_id) {
-               theSlot ();
-       } else {
-               ui.call_slot (theSlot);
-       }
-}
-
-template<class RequestType> 
-sigc::slot<void>
-crossthread_safe (pthread_t thread_id, AbstractUI<RequestType>& ui, sigc::slot<void> theSlot)
-{
-       /* this function returns a slot that will ensure that theSlot is either
-          called by the specified thread or passed to the interface via 
-          AbstractUI::call_slot().
-       */
-          
-       return sigc::bind (sigc::ptr_fun (call_slot_from_thread_or_dispatch_it<RequestType>), 
-                          thread_id, ui, theSlot);
-}
+#include <glibmm/main.h>
+
+class CrossThreadChannel { 
+  public:
+       CrossThreadChannel();
+       ~CrossThreadChannel();
+       
+       void wakeup();
+       int selectable() const { return fds[0]; }
+
+       void drain ();
+       static void drain (int fd);
+
+       Glib::RefPtr<Glib::IOSource> ios();
+       bool ok() const { return fds[0] >= 0 && fds[1] >= 0; }
+
+  private:
+       Glib::RefPtr<Glib::IOSource> _ios; // lazily constructed
+       int fds[2];
+};
 
 #endif /* __pbd__crossthread_h__ */
index dd91e0a2b1694745626236c03cd60753f7224238..15b37662d55a20aec34592f44c444521120c5120 100644 (file)
 
 #include <sigc++/sigc++.h>
 
-int  pthread_create_and_store (std::string name, pthread_t  *thread, pthread_attr_t *attr, void * (*start_routine)(void *), void * arg);
+int  pthread_create_and_store (std::string name, pthread_t  *thread, void * (*start_routine)(void *), void * arg);
 void pthread_cancel_one (pthread_t thread);
 void pthread_kill_all (int signum);
-void pthread_cancel_all ();
 void pthread_exit_pbd (void* status);
 std::string pthread_name ();
 
 namespace PBD {
-       extern void notify_gui_about_thread_creation (pthread_t, std::string, int requests = 256);
+       extern void notify_gui_about_thread_creation (std::string, pthread_t, std::string, int requests = 256);
        extern void notify_gui_about_thread_exit (pthread_t);
 
-       extern sigc::signal<void,pthread_t>             ThreadLeaving;
-       extern sigc::signal<void,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
+       extern sigc::signal<void,pthread_t>                                  ThreadLeaving;
+       extern sigc::signal<void,std::string,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
 }
 
 #endif /* __pbd_pthread_utils__ */
index 69270a767b1c50ad4953be1205b0784e5588ada4..68082e6136cdac3e98286e351ad60410d5ce67ee 100644 (file)
@@ -36,8 +36,8 @@ static pthread_mutex_t thread_map_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_mutex_t gui_notify_lock = PTHREAD_MUTEX_INITIALIZER;
 
 namespace PBD {
-   sigc::signal<void,pthread_t>             ThreadLeaving;
-   sigc::signal<void,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
+       sigc::signal<void,pthread_t>             ThreadLeaving;
+       sigc::signal<void,std::string, pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
 }
 
 using namespace PBD;
@@ -52,10 +52,10 @@ static int thread_creator (pthread_t* thread_id, const pthread_attr_t* attr, voi
 }
 
 void
-PBD::notify_gui_about_thread_creation (pthread_t thread, std::string str, int request_count)
+PBD::notify_gui_about_thread_creation (std::string target_gui, pthread_t thread, std::string str, int request_count)
 {
        pthread_mutex_lock (&gui_notify_lock);
-       ThreadCreatedWithRequestSize (thread, str, request_count);
+       ThreadCreatedWithRequestSize (target_gui, thread, str, request_count);
        pthread_mutex_unlock (&gui_notify_lock);
 }
 
@@ -68,35 +68,27 @@ PBD::notify_gui_about_thread_exit (pthread_t thread)
 }
 
 int  
-pthread_create_and_store (string name, pthread_t  *thread, pthread_attr_t *attr, void * (*start_routine)(void *), void * arg)
+pthread_create_and_store (string name, pthread_t  *thread, void * (*start_routine)(void *), void * arg)
 {
-       int ret;
-
        pthread_attr_t default_attr;
-       bool use_default_attr = (attr == NULL);
+       int ret;
        
-       if (use_default_attr) {
-               // set default stack size to sensible default for memlocking
-               pthread_attr_init(&default_attr);
-               pthread_attr_setstacksize(&default_attr, 500000);
-               attr = &default_attr;
-       }
+       // set default stack size to sensible default for memlocking
+       pthread_attr_init(&default_attr);
+       pthread_attr_setstacksize(&default_attr, 500000);
 
-       if ((ret = thread_creator (thread, attr, start_routine, arg)) == 0) {
+       if ((ret = thread_creator (thread, &default_attr, start_routine, arg)) == 0) {
                std::pair<string,pthread_t> newpair;
                newpair.first = name;
                newpair.second = *thread;
 
                pthread_mutex_lock (&thread_map_lock);
                all_threads.insert (newpair);
-
                pthread_mutex_unlock (&thread_map_lock);
        }
 
-       if (use_default_attr) {
-               pthread_attr_destroy(&default_attr);
-       }
-       
+       pthread_attr_destroy(&default_attr);
+
        return ret;
 }
 
@@ -131,19 +123,6 @@ pthread_kill_all (int signum)
        pthread_mutex_unlock (&thread_map_lock);
 }
 
-void
-pthread_cancel_all () 
-{      
-       pthread_mutex_lock (&thread_map_lock);
-       for (ThreadMap::iterator i = all_threads.begin(); i != all_threads.end(); ++i) {
-               if (i->second != pthread_self()) {
-                       pthread_cancel (i->second);
-               }
-       }
-       all_threads.clear();
-       pthread_mutex_unlock (&thread_map_lock);
-}
-
 void
 pthread_cancel_one (pthread_t thread) 
 {      
index 122f38da79b2d0c57934ea8caa94e22764443901..e114cbe1ae00fa3051988958e74834ceccd24434 100644 (file)
@@ -57,6 +57,7 @@ def build(bld):
                command.cc
                convert.cc
                controllable.cc
+                crossthread.cc
                enumwriter.cc
                dmalloc.cc
                error.cc
index 3ae5455a8cf9fe45ae2d1facb4ea5121726d78f2..d53c44b0a87b9a800c21d942e4b8b883cd506386 100644 (file)
@@ -53,7 +53,6 @@ BasicUI::register_thread (std::string name)
        std::string pool_name = name;
        pool_name += " events";
 
-       PBD::notify_gui_about_thread_creation (pthread_self(), name);
        SessionEvent::create_per_thread_pool (pool_name, 64);
 }
 
index cae9c23ae95d1bed3d9f13533401fb4889cc976a..d702b1a3a22be685e652bc6d7aa107e86ea3ca58 100644 (file)
@@ -23,7 +23,9 @@
 
 #include <string>
 
+
 #include <jack/types.h>
+
 #include "control_protocol/timecode.h"
 
 namespace ARDOUR {
index 2338e7c090c0f65ce9994e3ff4afeef768d54a13..8c30eb8e732e4bc2d44c6621f76198fc43d5259a 100644 (file)
@@ -1028,7 +1028,7 @@ TranzportControlProtocol::monitor_work ()
        uint8_t offline = 0;
 
 
-       PBD::notify_gui_about_thread_creation (pthread_self(), X_("Tranzport"));
+       PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("Tranzport"));
        pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
        pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
        next_track ();
index d93bf78d3393b5019af8b226bbd518566b47fc44..6e65308f33597da4c94044dde356c832d48472f8 100644 (file)
@@ -87,7 +87,7 @@ MackieControlProtocol::MackieControlProtocol (Session& session)
        cout << "MackieControlProtocol::MackieControlProtocol" << endl;
 #endif
        // will start reading from ports, as soon as there are some
-       pthread_create_and_store (X_("mackie monitor"), &thread, 0, _monitor_work, this);
+       pthread_create_and_store (X_("mackie monitor"), &thread, _monitor_work, this);
 }
 
 MackieControlProtocol::~MackieControlProtocol()
@@ -1423,7 +1423,7 @@ LedState MackieControlProtocol::clicking_release (Button &)
 LedState MackieControlProtocol::global_solo_press (Button &)
 {
        bool state = !session->soloing();
-       session->set_all_solo ( state );
+       session->set_solo (session->get_routes(), state);
        return state;
 }
 
index af067f65bcd95e602fc80d6846a29d4c06682f56..d2f45402e8eda111ee9810ab4964e27f21ca19ee 100644 (file)
@@ -302,15 +302,11 @@ OSC::init_osc_thread ()
                return false;
        }
        
-       pthread_attr_t attr;
-       pthread_attr_init(&attr);
-       pthread_attr_setstacksize(&attr, 500000);
+       pthread_create_and_store (X_("OSC"), &_osc_thread, &OSC::_osc_receiver, this);
 
-       pthread_create_and_store (X_("OSC"), &_osc_thread, &attr, &OSC::_osc_receiver, this);
        if (!_osc_thread) {
                return false;
        }
-       pthread_attr_destroy(&attr);
 
        //pthread_detach (_osc_thread);
        return true;
index 0cdfba6f3ae050e14b62693770c30a732f4923ee..31154ad879aef80216e0bcd92ba2e5aa0fbb6f2e 100644 (file)
@@ -129,7 +129,7 @@ PowermateControlProtocol::set_active (bool inActivate)
                                return -1;
                        }
                        
-                       if (pthread_create_and_store ("Powermate", &mThread, 0, SerialThreadEntry, this) == 0) {
+                       if (pthread_create_and_store ("Powermate", &mThread, SerialThreadEntry, this) == 0) {
                                _active = true;
                        } else {
                                return -1;
index 910d37a5de3c54535b4c10518f3592471197adf8..7d6ee2220613d36a959d29a9226415ff5aef306a 100644 (file)
@@ -86,16 +86,16 @@ TranzportControlProtocol::set_active (bool yn)
                                return -1;
                        }
 
-                       if (pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this) == 0) {
+                       if (pthread_create_and_store (X_("tranzport monitor"), &thread, _monitor_work, this) == 0) {
                                _active = true;
 #if TRANZPORT_THREADS                      
-                       if (pthread_create_and_store (X_("tranzport read"), &thread_read, 0, _read_work, this) == 0) {
+                       if (pthread_create_and_store (X_("tranzport read"), &thread_read, _read_work, this) == 0) {
                                _active_read = true;
-                       if (pthread_create_and_store (X_("tranzport write"), &thread_write, 0, _write_work, this) == 0) {
+                       if (pthread_create_and_store (X_("tranzport write"), &thread_write, _write_work, this) == 0) {
                                _active_write = true;
-                       if (pthread_create_and_store (X_("tranzport process"), &thread_process, 0, _process_work, this) == 0) {
+                       if (pthread_create_and_store (X_("tranzport process"), &thread_process, _process_work, this) == 0) {
                                _active_process = true;
-                       if (pthread_create_and_store (X_("tranzport timer"), &thread_timer, 0, _process_timer, this) == 0) {
+                       if (pthread_create_and_store (X_("tranzport timer"), &thread_timer, _process_timer, this) == 0) {
                                _active_process = true;
 #endif
                        } else {