/* Copyright (C) 1999 Paul Barton-Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: gtk_ui.h,v 1.15 2005/04/05 13:05:29 pauld Exp $ */ #ifndef __pbd_gtk_ui_h__ #define __pbd_gtk_ui_h__ #include <string> #include <queue> #include <map> #include <pthread.h> #include <gtk--.h> #include <pbd/abstract_ui.h> #include <pbd/ringbufferNPT.h> #include <pbd/atomic.h> #include <pbd/pool.h> #include <pbd/error.h> #include <pbd/lockmonitor.h> using std::string; using std::queue; class Touchable; namespace Gtkmmext { class TextViewer; class UI : public AbstractUI { public: UI (string name, int *argc, char **argv[], string rcfile); virtual ~UI (); static UI *instance() { return theGtkUI; } /* Abstract UI interfaces */ bool running (); void quit (); void kill (); int load_rcfile (string); void request (RequestType); void run (Receiver &old_receiver); void call_slot (SigC::Slot0<void>); void call_slot_locked (SigC::Slot0<void>); void touch_display (Touchable *); void receive (Transmitter::Channel, const char *); void register_thread (pthread_t, string); bool caller_is_gui_thread () { return pthread_equal (gui_thread, pthread_self()); } /* Gtk-UI specific interfaces */ void set_tip (Gtk::Widget *, const gchar *txt, const gchar *hlp = 0); void set_state (Gtk::Widget *w, GtkStateType 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 (); template<class T> static gint idle_delete (T *obj) { delete obj; return false; } template<class T> static void delete_when_idle (T *obj) { Gtk::Main::idle.connect (bind (slot (&UI::idle_delete<T>), obj)); } GdkColor get_color (const string& prompt, bool& picked, gdouble *initial = 0); /* starting is sent just before we enter the main loop, stopping just after we return from it (at the top level) */ SigC::Signal0<void> starting; SigC::Signal0<void> stopping; static gint just_hide_it (GdkEventAny *, Gtk::Window *); protected: virtual void handle_fatal (const char *); virtual void display_message (const char *prefix, gint prefix_len, GtkStyle *style, 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; GtkStateType new_state; int (*function)(void *); Gtk::Widget *widget; Transmitter::Channel chn; void *arg; const char *msg2; unsigned int timeout; SigC::Slot0<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; /* error message display styles */ GtkStyle *error_message_style; GtkStyle *info_message_style; GtkStyle *warning_message_style; GtkStyle *fatal_message_style; 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 (); void color_selection_done (bool status); gint color_selection_deleted (GdkEventAny *); bool color_picked; }; 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__ */