#include #include #include #include #include #include #include #include #include using namespace std; using namespace SigC; using namespace SigCX; using namespace SigCX::Threads; // Incrementor. // // Simply call a slot with an increasing value. In a real // application, you'd do some real work in this class. We want to run // in our own thread, so we derive from StandardDispatcher. // class Incrementor : public StandardDispatcher { public: Incrementor(Slot1 s) : count_(0), slot_(s) { } private: void do_increment() { slot_(count_++); // we have ourselves called again in 250 milliseconds add_timeout_handler_msec(slot(*this, &Incrementor::do_increment), 250); } // Run method, overrides StandardDispatcher::run virtual bool run(bool infinite = true) { // We start with do_increment(), since that registers our timeout // handler do_increment(); return StandardDispatcher::run(infinite); } int count_; Slot1 slot_; }; // // Application window // class MyWindow: public Gtk::Window { Gtk::Label label_[2]; // We need a GtkDispatcher here, since we run on the GTK+ main // loop. GtkDispatcher disp_; ThreadTunnel my_tunnel_; ThreadTunnel *thread_tunnel_[2]; Incrementor *incrementor_[2]; public: MyWindow(); virtual ~MyWindow(); protected: void set_label(int i, int label_idx); void launch_threads(); void sig_handler(int sig); bool on_delete(GdkEventAny*); }; MyWindow::MyWindow() : // We need a tunnel to our dispatcher, so the threads can talk to // us my_tunnel_(disp_, ThreadTunnel::CurrentThread) { // one-shot idle handler to start the threads Glib::signal_idle().connect( bind_return(slot(*this, &MyWindow::launch_threads), false)); // connect to SIGINT signal, so we can do a clean shutdown disp_.add_signal_handler(bind(slot(*this, &MyWindow::sig_handler), SIGINT), SIGINT); // we want to react on window-delete, too signal_delete_event().connect(slot(*this, &MyWindow::on_delete), false); // Create our 2 incrementors, both operating on a tunneled slot to // our set_label() method. One slot is synchronous, the other // asynchronous (last argument to open_tunnel). for (int i = 0; i < 2; i++) { Slot1 tslot = open_tunnel( &my_tunnel_, bind(slot(*this, &MyWindow::set_label), i), (i % 2 == 0) ? true : false); incrementor_[i] = new Incrementor(tslot); thread_tunnel_[i] = 0; } // Construct our GUI elements: Two labels - very funky ;-) Gtk::HBox* hbox = manage(new Gtk::HBox(true, 10)); hbox->set_border_width(5); hbox->pack_start(label_[0]); hbox->pack_start(label_[1]); add(*hbox); show_all_children(); } MyWindow::~MyWindow() { // We drain the tunnel to ourselves, so any messages pending and // sent now are thrown away. my_tunnel_.drain(); // We send exit messages to our incrementors (synchronously, so we // know their dispatcher is exiting when we return (and cease to // exist, along with our tunnel) for (int i = 0; i < 2; i++) { tunnel(slot(dynamic_cast(*incrementor_[i]), &Dispatcher::exit), thread_tunnel_[i], true); if (thread_tunnel_[i]) delete thread_tunnel_[i]; delete incrementor_[i]; } } bool MyWindow::on_delete(GdkEventAny*) { // make the dispatcher exit (this ends the GTK+ event loop) disp_.exit(); return true; } void MyWindow::set_label(int i, int label_idx) { ostringstream os; os << i; label_[label_idx].set_text(os.str()); } void MyWindow::launch_threads() { for (int i = 0; i < 2; i++) thread_tunnel_[i] = new ThreadTunnel(*incrementor_[i]); } void MyWindow::sig_handler(int sig) { // we need to take precausions to not exit more than once, since on // Linux, all threads receive a signal static volatile sig_atomic_t did_exit_ = 0; if (sig == SIGINT && !did_exit_) { did_exit_ = 1; cout << "SIGINT caught - exiting" << endl; disp_.exit(); } } int main(int argc, char* argv[]) { Gtk::Main main(argc, argv); MyWindow window; window.show(); main.run(); return 0; }