From 028e1ebc4a392572cae586d0e9044a32b867cba4 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 24 Apr 2006 22:45:19 +0000 Subject: 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 --- libs/gtkmm2ext/gtk_ui.cc | 380 ++++++++--------------------------------------- 1 file changed, 64 insertions(+), 316 deletions(-) (limited to 'libs/gtkmm2ext/gtk_ui.cc') 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 /* instantiate the template */ + + +UI::UI (string namestr, int *argc, char ***argv, string rcfile) + : AbstractUI (namestr, true) { theMain = new Main (argc, argv); tips = new Tooltips; - if (pthread_key_create (&thread_request_buffer_key, 0)) { - cerr << _("cannot create thread request buffer key") << endl; - throw failed_constructor(); - } - - PBD::ThreadCreated.connect (mem_fun (*this, &UI::register_thread)); - PBD::ThreadCreatedWithRequestSize.connect (mem_fun (*this, &UI::register_thread_with_request_count)); - - _ok = false; _active = false; if (!theGtkUI) { @@ -73,33 +75,39 @@ UI::UI (string name, int *argc, char ***argv, string rcfile) /* NOTREACHED */ } - if (setup_signal_pipe ()) { - return; - } + /* add the pipe to the select/poll loop that GDK does */ + + gdk_input_add (signal_pipe[0], + GDK_INPUT_READ, + UI::signal_pipe_callback, + this); errors = new TextViewer (850,100); errors->text().set_editable (false); errors->text().set_name ("ErrorText"); string title; - title = _ui_name; + title = namestr; title += ": Log"; errors->set_title (title); errors->dismiss_button().set_name ("ErrorLogCloseButton"); - errors->signal_delete_event().connect (bind (ptr_fun (just_hide_it), (Window *) errors)); + errors->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors)); register_thread (pthread_self(), X_("GUI")); load_rcfile (rcfile); - - _ok = true; } UI::~UI () { - close (signal_pipe[0]); - close (signal_pipe[1]); +} + + +bool +UI::caller_is_ui_thread () +{ + return pthread_equal (gui_thread, pthread_self()); } int @@ -226,7 +234,13 @@ UI::kill () void UI::quit () { - request (Quit); + UIRequest *req = get_request (Quit); + + if (req == 0) { + return; + } + + send_request (req); } static bool idle_quit () @@ -241,16 +255,10 @@ UI::do_quit () Glib::signal_idle().connect (sigc::ptr_fun (idle_quit)); } -int -UI::set_quit_context() -{ - return setjmp (quit_context); -} - void UI::touch_display (Touchable *display) { - Request *req = get_request (TouchDisplay); + UIRequest *req = get_request (TouchDisplay); if (req == 0) { return; @@ -261,52 +269,10 @@ UI::touch_display (Touchable *display) send_request (req); } -void -UI::call_slot (sigc::slot slot) -{ - Request *req = get_request (CallSlot); - - if (req == 0) { - return; - } - - req->slot = slot; - - send_request (req); -} - -void -UI::call_slot_locked (sigc::slot 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* b = new RingBufferNPT (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* rbuf = static_cast* >(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::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::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(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* rbuf = static_cast *> (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; -- cgit v1.2.3