diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2006-04-24 22:45:19 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2006-04-24 22:45:19 +0000 |
commit | 028e1ebc4a392572cae586d0e9044a32b867cba4 (patch) | |
tree | 36d3a748486feb3f41575708bef8b153fef2cad4 /libs/gtkmm2ext | |
parent | 484debb45c5ea45bccf0f9cb05b1239a9c2244a3 (diff) |
a) completely refactor abstract UI code
b) single-thread Tranzport implementation
c) implement BasicUI to share functionality across multiple
controllers
d) various minor fixes here and there
git-svn-id: svn://localhost/trunk/ardour2@468 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/gtkmm2ext')
-rw-r--r-- | libs/gtkmm2ext/gtk_ui.cc | 380 | ||||
-rw-r--r-- | libs/gtkmm2ext/gtkmm2ext/gtk_ui.h | 159 |
2 files changed, 114 insertions, 425 deletions
diff --git a/libs/gtkmm2ext/gtk_ui.cc b/libs/gtkmm2ext/gtk_ui.cc index 8dd5a18526..50d6114dee 100644 --- a/libs/gtkmm2ext/gtk_ui.cc +++ b/libs/gtkmm2ext/gtk_ui.cc @@ -48,21 +48,23 @@ using std::map; pthread_t UI::gui_thread; UI *UI::theGtkUI = 0; -UI::UI (string name, int *argc, char ***argv, string rcfile) - : _ui_name (name) +BaseUI::RequestType Gtkmm2ext::ErrorMessage = BaseUI::new_request_type(); +BaseUI::RequestType Gtkmm2ext::Quit = BaseUI::new_request_type(); +BaseUI::RequestType Gtkmm2ext::TouchDisplay = BaseUI::new_request_type(); +BaseUI::RequestType Gtkmm2ext::StateChange = BaseUI::new_request_type(); +BaseUI::RequestType Gtkmm2ext::SetTip = BaseUI::new_request_type(); +BaseUI::RequestType Gtkmm2ext::AddIdle = BaseUI::new_request_type(); +BaseUI::RequestType Gtkmm2ext::AddTimeout = BaseUI::new_request_type(); + +#include <pbd/abstract_ui.cc> /* instantiate the template */ + + +UI::UI (string namestr, int *argc, char ***argv, string rcfile) + : AbstractUI<UIRequest> (namestr, true) { theMain = new Main (argc, argv); tips = new Tooltips; - if (pthread_key_create (&thread_request_buffer_key, 0)) { - cerr << _("cannot create thread request buffer key") << endl; - throw failed_constructor(); - } - - PBD::ThreadCreated.connect (mem_fun (*this, &UI::register_thread)); - PBD::ThreadCreatedWithRequestSize.connect (mem_fun (*this, &UI::register_thread_with_request_count)); - - _ok = false; _active = false; if (!theGtkUI) { @@ -73,33 +75,39 @@ UI::UI (string name, int *argc, char ***argv, string rcfile) /* NOTREACHED */ } - if (setup_signal_pipe ()) { - return; - } + /* add the pipe to the select/poll loop that GDK does */ + + gdk_input_add (signal_pipe[0], + GDK_INPUT_READ, + UI::signal_pipe_callback, + this); errors = new TextViewer (850,100); errors->text().set_editable (false); errors->text().set_name ("ErrorText"); string title; - title = _ui_name; + title = namestr; title += ": Log"; errors->set_title (title); errors->dismiss_button().set_name ("ErrorLogCloseButton"); - errors->signal_delete_event().connect (bind (ptr_fun (just_hide_it), (Window *) errors)); + errors->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors)); register_thread (pthread_self(), X_("GUI")); load_rcfile (rcfile); - - _ok = true; } UI::~UI () { - close (signal_pipe[0]); - close (signal_pipe[1]); +} + + +bool +UI::caller_is_ui_thread () +{ + return pthread_equal (gui_thread, pthread_self()); } int @@ -226,7 +234,13 @@ UI::kill () void UI::quit () { - request (Quit); + UIRequest *req = get_request (Quit); + + if (req == 0) { + return; + } + + send_request (req); } static bool idle_quit () @@ -241,16 +255,10 @@ UI::do_quit () Glib::signal_idle().connect (sigc::ptr_fun (idle_quit)); } -int -UI::set_quit_context() -{ - return setjmp (quit_context); -} - void UI::touch_display (Touchable *display) { - Request *req = get_request (TouchDisplay); + UIRequest *req = get_request (TouchDisplay); if (req == 0) { return; @@ -262,51 +270,9 @@ UI::touch_display (Touchable *display) } void -UI::call_slot (sigc::slot<void> slot) -{ - Request *req = get_request (CallSlot); - - if (req == 0) { - return; - } - - req->slot = slot; - - send_request (req); -} - -void -UI::call_slot_locked (sigc::slot<void> slot) -{ - if (caller_is_gui_thread()) { - call_slot (slot); - return; - } - - Request *req = get_request (CallSlotLocked); - - if (req == 0) { - return; - } - - req->slot = slot; - - pthread_mutex_init (&req->slot_lock, NULL); - pthread_cond_init (&req->slot_cond, NULL); - pthread_mutex_lock (&req->slot_lock); - - send_request (req); - - pthread_cond_wait (&req->slot_cond, &req->slot_lock); - pthread_mutex_unlock (&req->slot_lock); - - delete req; -} - -void UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp) { - Request *req = get_request (SetTip); + UIRequest *req = get_request (SetTip); if (req == 0) { return; @@ -322,7 +288,7 @@ UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp) void UI::set_state (Widget *w, StateType state) { - Request *req = get_request (StateChange); + UIRequest *req = get_request (StateChange); if (req == 0) { return; @@ -337,22 +303,7 @@ UI::set_state (Widget *w, StateType state) void UI::idle_add (int (*func)(void *), void *arg) { - Request *req = get_request (AddIdle); - - if (req == 0) { - return; - } - - req->function = func; - req->arg = arg; - - send_request (req); -} - -void -UI::timeout_add (unsigned int timeout, int (*func)(void *), void *arg) -{ - Request *req = get_request (AddTimeout); + UIRequest *req = get_request (AddIdle); if (req == 0) { return; @@ -360,113 +311,12 @@ UI::timeout_add (unsigned int timeout, int (*func)(void *), void *arg) req->function = func; req->arg = arg; - req->timeout = timeout; send_request (req); } /* END abstract_ui interfaces */ -/* Handling requests */ - -void -UI::register_thread (pthread_t thread_id, string name) -{ - register_thread_with_request_count (thread_id, name, 256); -} - -void -UI::register_thread_with_request_count (pthread_t thread_id, string name, uint32_t num_requests) -{ - RingBufferNPT<Request>* b = new RingBufferNPT<Request> (num_requests); - - { - PBD::LockMonitor lm (request_buffer_map_lock, __LINE__, __FILE__); - request_buffers[thread_id] = b; - } - - pthread_setspecific (thread_request_buffer_key, b); -} - -UI::Request::Request() -{ - -} - -UI::Request* -UI::get_request (RequestType rt) -{ - RingBufferNPT<Request>* rbuf = static_cast<RingBufferNPT<Request>* >(pthread_getspecific (thread_request_buffer_key)); - - if (rbuf == 0) { - /* Cannot happen, but if it does we can't use the error reporting mechanism */ - cerr << _("programming error: ") - << string_compose (X_("no GUI request buffer found for thread %1"), pthread_self()) - << endl; - abort (); - } - - RingBufferNPT<Request>::rw_vector vec; - - rbuf->get_write_vector (&vec); - - if (vec.len[0] == 0) { - if (vec.len[1] == 0) { - cerr << string_compose (X_("no space in GUI request buffer for thread %1"), pthread_self()) - << endl; - return 0; - } else { - vec.buf[1]->type = rt; - return vec.buf[1]; - } - } else { - vec.buf[0]->type = rt; - return vec.buf[0]; - } -} - -int -UI::setup_signal_pipe () -{ - /* setup the pipe that other threads send us notifications/requests - through. - */ - - if (pipe (signal_pipe)) { - error << "UI: cannot create error signal pipe (" - << std::strerror (errno) << ")" - << endmsg; - - return -1; - } - - if (fcntl (signal_pipe[0], F_SETFL, O_NONBLOCK)) { - error << "UI: cannot set O_NONBLOCK on " - "signal read pipe (" - << std::strerror (errno) << ")" - << endmsg; - return -1; - } - - if (fcntl (signal_pipe[1], F_SETFL, O_NONBLOCK)) { - error << "UI: cannot set O_NONBLOCK on " - "signal write pipe (" - << std::strerror (errno) - << ")" - << endmsg; - return -1; - } - - /* add the pipe to the select/poll loop that GDK does */ - - gdk_input_add (signal_pipe[0], - GDK_INPUT_READ, - UI::signal_pipe_callback, - this); - - return 0; -} - void UI::signal_pipe_callback (void *arg, int fd, GdkInputCondition cond) { @@ -480,145 +330,43 @@ UI::signal_pipe_callback (void *arg, int fd, GdkInputCondition cond) } void -UI::handle_ui_requests () +UI::do_request (UIRequest* req) { - RequestBufferMap::iterator i; - - request_buffer_map_lock.lock (); - - for (i = request_buffers.begin(); i != request_buffers.end(); ++i) { - - RingBufferNPT<Request>::rw_vector vec; - - while (true) { - - /* we must process requests 1 by 1 because - the request may run a recursive main - event loop that will itself call - handle_ui_requests. when we return - from the request handler, we cannot - expect that the state of queued requests - is even remotely consistent with - the condition before we called it. - */ - - i->second->get_read_vector (&vec); - - if (vec.len[0] == 0) { - break; - } else { - /* copy constructor does a deep - copy of the Request object, - unlike Ringbuffer::read() - */ - Request req (*vec.buf[0]); - i->second->increment_read_ptr (1); - request_buffer_map_lock.unlock (); - do_request (&req); - request_buffer_map_lock.lock (); - } - } - } - - request_buffer_map_lock.unlock (); -} + if (req->type == ErrorMessage) { -void -UI::do_request (Request* req) -{ - switch (req->type) { - case ErrorMessage: process_error_message (req->chn, req->msg); free (const_cast<char*>(req->msg)); /* it was strdup'ed */ req->msg = 0; /* don't free it again in the destructor */ - break; - - case Quit: + + } else if (req->type == Quit) { + do_quit (); - break; - - case CallSlot: - req->slot (); - break; - case CallSlotLocked: - pthread_mutex_lock (&req->slot_lock); + } else if (req->type == CallSlot) { + req->slot (); - pthread_cond_signal (&req->slot_cond); - pthread_mutex_unlock (&req->slot_lock); - break; - - case TouchDisplay: + + } else if (req->type == TouchDisplay) { + req->display->touch (); if (req->display->delete_after_touch()) { delete req->display; } - break; - - case StateChange: + + } else if (req->type == StateChange) { + req->widget->set_state (req->new_state); - break; - - case SetTip: - /* XXX need to figure out how this works */ - break; - - case AddIdle: - gtk_idle_add (req->function, req->arg); - break; - - case AddTimeout: - gtk_timeout_add (req->timeout, req->function, req->arg); - break; - - default: - error << "UI: unknown request type " - << (int) req->type - << endmsg; - } -} -void -UI::send_request (Request *req) -{ - if (instance() == 0) { - return; /* XXX is this the right thing to do ? */ - } - - if (caller_is_gui_thread()) { - // cerr << "GUI thread sent request " << req << " type = " << req->type << endl; - do_request (req); - } else { - const char c = 0; - RingBufferNPT<Request*>* rbuf = static_cast<RingBufferNPT<Request*> *> (pthread_getspecific (thread_request_buffer_key)); - - if (rbuf == 0) { - /* can't use the error system to report this, because this - thread isn't registered! - */ - cerr << _("programming error: ") - << string_compose (X_("UI::send_request() called from %1, but no request buffer exists for that thread"), - pthread_self()) - << endl; - abort (); - } - - // cerr << "thread " << pthread_self() << " sent request " << req << " type = " << req->type << endl; - rbuf->increment_write_ptr (1); - write (signal_pipe[1], &c, 1); - } -} + } else if (req->type == SetTip) { -void -UI::request (RequestType rt) -{ - Request *req = get_request (rt); + /* XXX need to figure out how this works */ - if (req == 0) { - return; - } + } else { - send_request (req); + error << "GtkUI: unknown request type " + << (int) req->type + << endmsg; + } } /*====================================================================== @@ -628,10 +376,10 @@ UI::request (RequestType rt) void UI::receive (Transmitter::Channel chn, const char *str) { - if (caller_is_gui_thread()) { + if (caller_is_ui_thread()) { process_error_message (chn, str); } else { - Request* req = get_request (ErrorMessage); + UIRequest* req = get_request (ErrorMessage); if (req == 0) { return; @@ -763,7 +511,7 @@ UI::handle_fatal (const char *message) win.set_default_size (400, 100); string title; - title = _ui_name; + title = name(); title += ": Fatal Error"; win.set_title (title); @@ -787,7 +535,7 @@ UI::popup_error (const char *text) { PopUp *pup; - if (!caller_is_gui_thread()) { + if (!caller_is_ui_thread()) { error << "non-UI threads can't use UI::popup_error" << endmsg; return; @@ -802,7 +550,7 @@ UI::popup_error (const char *text) void UI::flush_pending () { - if (!caller_is_gui_thread()) { + if (!caller_is_ui_thread()) { error << "non-UI threads cannot call UI::flush_pending()" << endmsg; return; diff --git a/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h b/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h index b9f7c31e96..5446374d7b 100644 --- a/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h +++ b/libs/gtkmm2ext/gtkmm2ext/gtk_ui.h @@ -39,6 +39,7 @@ #include <pbd/atomic.h> #include <pbd/pool.h> #include <pbd/error.h> +#include <pbd/receiver.h> #include <pbd/lockmonitor.h> using std::string; @@ -50,8 +51,42 @@ namespace Gtkmm2ext { class TextViewer; -class UI : public AbstractUI +extern BaseUI::RequestType ErrorMessage; +extern BaseUI::RequestType Quit; +extern BaseUI::RequestType CallSlot; +extern BaseUI::RequestType TouchDisplay; +extern BaseUI::RequestType StateChange; +extern BaseUI::RequestType SetTip; +extern BaseUI::RequestType AddIdle; +extern BaseUI::RequestType AddTimeout; + +struct UIRequest : public BaseUI::BaseRequestObject { + + /* this once used anonymous unions to merge elements + that are never part of the same request. that makes + the creation of a legal copy constructor difficult + because of the semantics of the slot member. + */ + + Touchable *display; + const char *msg; + Gtk::StateType new_state; + int (*function)(void *); + Gtk::Widget *widget; + Transmitter::Channel chn; + void *arg; + const char *msg2; + sigc::slot<void> slot; + + ~UIRequest () { + if (type == ErrorMessage && msg) { + /* msg was strdup()'ed */ + free ((char *)msg); + } + } + }; +class UI : public Receiver, public AbstractUI<UIRequest> { public: UI (string name, int *argc, char **argv[], string rcfile); @@ -59,36 +94,29 @@ class UI : public AbstractUI static UI *instance() { return theGtkUI; } + /* receiver interface */ + + void receive (Transmitter::Channel, const char *); + /* Abstract UI interfaces */ + bool caller_is_ui_thread (); + + /* Gtk-UI specific interfaces */ + bool running (); void quit (); void kill (); int load_rcfile (string); - void request (RequestType); void run (Receiver &old_receiver); - void call_slot (sigc::slot<void>); - void call_slot_locked (sigc::slot<void>); - void touch_display (Touchable *); - void receive (Transmitter::Channel, const char *); - - void register_thread (pthread_t, string); - void register_thread_with_request_count (pthread_t, string, uint32_t num_requests); - - bool caller_is_gui_thread () { - return pthread_equal (gui_thread, pthread_self()); - } - /* Gtk-UI specific interfaces */ - - int set_quit_context (); - void set_tip (Gtk::Widget *, const gchar *txt, const gchar *hlp = 0); void set_state (Gtk::Widget *w, Gtk::StateType state); - void idle_add (int (*)(void *), void *); - void timeout_add (unsigned int, int (*)(void *), void *); void popup_error (const char *text); void flush_pending (); void toggle_errors (); + void touch_display (Touchable *); + void set_tip (Gtk::Widget *w, const gchar *tip, const gchar *hlp); + void idle_add (int (*func)(void *), void *arg); template<class T> static bool idle_delete (T *obj) { delete obj; return false; } template<class T> static void delete_when_idle (T *obj) { @@ -106,6 +134,8 @@ class UI : public AbstractUI static bool just_hide_it (GdkEventAny *, Gtk::Window *); + static pthread_t the_gui_thread() { return gui_thread; } + protected: virtual void handle_fatal (const char *); virtual void display_message (const char *prefix, gint prefix_len, @@ -113,58 +143,10 @@ class UI : public AbstractUI Glib::RefPtr<Gtk::TextBuffer::Tag> mtag, const char *msg); - /* stuff to invoke member functions in another - thread so that we can keep the GUI running. - */ - - template<class UI_CLASS> struct thread_arg { - UI_CLASS *ui; - void (UI_CLASS::*func)(void *); - void *arg; - }; - - template<class UI_CLASS> static void *start_other_thread (void *arg); - template<class UI_CLASS> void other_thread (void (UI_CLASS::*func)(void *), void *arg = 0); - private: - struct Request { - - /* this once used anonymous unions to merge elements - that are never part of the same request. that makes - the creation of a legal copy constructor difficult - because of the semantics of the slot member. - */ - - RequestType type; - Touchable *display; - const char *msg; - Gtk::StateType new_state; - int (*function)(void *); - Gtk::Widget *widget; - Transmitter::Channel chn; - void *arg; - const char *msg2; - unsigned int timeout; - sigc::slot<void> slot; - - /* this is for CallSlotLocked requests */ - - pthread_mutex_t slot_lock; - pthread_cond_t slot_cond; - - Request (); - ~Request () { - if (type == ErrorMessage && msg) { - /* msg was strdup()'ed */ - free ((char *)msg); - } - } - }; - static UI *theGtkUI; static pthread_t gui_thread; bool _active; - string _ui_name; Gtk::Main *theMain; Gtk::Tooltips *tips; TextViewer *errors; @@ -177,18 +159,6 @@ class UI : public AbstractUI Glib::RefPtr<Gtk::TextBuffer::Tag> warning_ptag; Glib::RefPtr<Gtk::TextBuffer::Tag> warning_mtag; - int signal_pipe[2]; - PBD::Lock request_buffer_map_lock; - typedef std::map<pthread_t,RingBufferNPT<Request>* > RequestBufferMap; - RequestBufferMap request_buffers; - Request* get_request(RequestType); - pthread_key_t thread_request_buffer_key; - - int setup_signal_pipe (); - - void handle_ui_requests (); - void do_request (Request *); - void send_request (Request *); static void signal_pipe_callback (void *, gint, GdkInputCondition); void process_error_message (Transmitter::Channel, const char *); void do_quit (); @@ -197,38 +167,9 @@ class UI : public AbstractUI bool color_selection_deleted (GdkEventAny *); bool color_picked; - jmp_buf quit_context; + void do_request (UIRequest*); }; -template<class UI_CLASS> void * -UI::start_other_thread (void *arg) - -{ - thread_arg<UI_CLASS> *ta = (thread_arg<UI_CLASS> *) arg; - (ta->ui->*ta->func)(ta->arg); - delete ta; - return 0; -} - -template<class UI_CLASS> void -UI::other_thread (void (UI_CLASS::*func)(void *), void *arg) - -{ - pthread_t thread_id; - thread_arg<UI_CLASS> *ta = new thread_arg<UI_CLASS>; - - ta->ui = dynamic_cast<UI_CLASS *> (this); - if (ta->ui == 0) { - error << "UI::other thread called illegally" - << endmsg; - return; - } - ta->func = func; - ta->arg = arg; - pthread_create (&thread_id, 0, start_other_thread, ta); -} - - } /* namespace */ #endif /* __pbd_gtk_ui_h__ */ |