diff --git a/c/glibmm-test/CMakeLists.txt b/c/glibmm-test/CMakeLists.txt new file mode 100644 index 0000000..5ddb0f2 --- /dev/null +++ b/c/glibmm-test/CMakeLists.txt @@ -0,0 +1,29 @@ +project (glibmm-test) + +cmake_minimum_required (VERSION 2.6) + +set (CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + +include (FindPkgConfig) +include (cmake_FindGlib) + +include_directories (${GLIB_INCLUDE_DIR}) +include_directories (${GLIB_PKG_INCLUDE_DIRS}) + +# include (FindGTK2) # doesn't work on WIN32 while +find_package (PkgConfig REQUIRED) + +pkg_check_modules (GTK2 REQUIRED gtk+-2.0) +pkg_check_modules(GTKMM gtkmm-2.4 glibmm-2.4) + +include_directories (${GTK2_INCLUDE_DIRS}) +link_directories (${GTK2_LIBRARY_DIRS}) +add_definitions (${GTK2_CFLAGS_OTHER}) + +include_directories (${GTKMM_INCLUDE_DIRS}) +link_directories (${GTKMM_LIBRARY_DIRS}) +add_definitions (${GTKMM_CFLAGS_OTHER}) + +# add the executable +add_executable (glibmm-test glibmm-test.cxx) +target_link_libraries (glibmm-test ${GTK2_LIBRARIES} ${GTKMM_LIBRARIES}) diff --git a/c/glibmm-test/glibmm-test.cxx b/c/glibmm-test/glibmm-test.cxx new file mode 100644 index 0000000..41f12b7 --- /dev/null +++ b/c/glibmm-test/glibmm-test.cxx @@ -0,0 +1,218 @@ +/* + * Glib::Dispatcher example -- cross thread signalling + * by Daniel Elstner + * + * modified to only use glibmm + * by J. Abelardo Gutierrez + * + * Copyright (c) 2002-2003 Free Software Foundation + */ + +#include + +#include +#include +#include +#include + +namespace +{ + +/* + * Note that it does not make sense for this class to inherit from + * sigc::trackable, as doing so would only give a false sense of security. + * Once the thread launch has been triggered, the object has to stay alive + * until the thread has been joined again. The code running in the thread + * assumes the existence of the object. If it is destroyed earlier, the + * program will crash, with sigc::trackable or without it. + */ +class ThreadProgress +{ +public: + explicit ThreadProgress(int id); + virtual ~ThreadProgress(); + + int id() const; + void launch(); + void join(); + bool unfinished() const; + + sigc::signal& signal_finished(); + +private: + enum { ITERATIONS = 100 }; + + // Note that the thread does not write to the member data at all. It only + // reads signal_increment_, which is only written to before the thread is + // launched. Therefore, no locking is required. + Glib::Threads::Thread* thread_; + int id_; + unsigned int progress_; + Glib::Dispatcher signal_increment_; + sigc::signal signal_finished_; + + void progress_increment(); + void thread_function(); +}; + +class Application : public sigc::trackable +{ +public: + Application(); + virtual ~Application(); + + void run(); + +private: + Glib::RefPtr main_loop_; + std::vector progress_threads_; + + void launch_threads(); + void on_progress_finished(ThreadProgress* thread_progress); +}; + +template +class DeletePtr : public std::unary_function +{ +public: + void operator()(T ptr) const { delete ptr; } +}; + +ThreadProgress::ThreadProgress(int id) +: + thread_ (0), + id_ (id), + progress_ (0) +{ + // Connect to the cross-thread signal. + signal_increment_.connect(sigc::mem_fun(*this, &ThreadProgress::progress_increment)); +} + +ThreadProgress::~ThreadProgress() +{ + // It is an error if the thread is still running at this point. + g_return_if_fail(thread_ == 0); +} + +int ThreadProgress::id() const +{ + return id_; +} + +void ThreadProgress::launch() +{ + // Create a joinable thread. + thread_ = Glib::Threads::Thread::create(sigc::mem_fun(*this, &ThreadProgress::thread_function)); +} + +void ThreadProgress::join() +{ + thread_->join(); + thread_ = 0; +} + +bool ThreadProgress::unfinished() const +{ + return (progress_ < ITERATIONS); +} + +sigc::signal& ThreadProgress::signal_finished() +{ + return signal_finished_; +} + +void ThreadProgress::progress_increment() +{ + ++progress_; + std::cout << "Thread " << id_ << ": " << progress_ << '%' << std::endl; + + if (progress_ >= ITERATIONS) + signal_finished_(); +} + +void ThreadProgress::thread_function() +{ + Glib::Rand rand; + + for (int i = 0; i < ITERATIONS; ++i) + { + Glib::usleep(rand.get_int_range(2000, 20000)); + + // Tell the main thread to increment the progress value. + signal_increment_(); + } +} + +Application::Application() +: + main_loop_ (Glib::MainLoop::create()), + progress_threads_ (5) +{ + try + { + for (std::vector::size_type i = 0; i < progress_threads_.size(); ++i) + { + ThreadProgress *const progress = new ThreadProgress(i + 1); + progress_threads_[i] = progress; + + progress->signal_finished().connect( + sigc::bind<1>(sigc::mem_fun(*this, &Application::on_progress_finished), progress)); + } + } + catch (...) + { + // In your own code, you should preferably use a smart pointer + // to ensure exception safety. + std::for_each(progress_threads_.begin(), progress_threads_.end(), + DeletePtr()); + throw; + } +} + +Application::~Application() +{ + std::for_each(progress_threads_.begin(), progress_threads_.end(), + DeletePtr()); +} + +void Application::run() +{ + // Install a one-shot idle handler to launch the threads. + Glib::signal_idle().connect_once(sigc::mem_fun(*this, &Application::launch_threads)); + + main_loop_->run(); +} + +void Application::launch_threads() +{ + std::cout << "Launching " << progress_threads_.size() << " threads:" << std::endl; + + std::for_each(progress_threads_.begin(), progress_threads_.end(), + std::mem_fun(&ThreadProgress::launch)); +} + +void Application::on_progress_finished(ThreadProgress* thread_progress) +{ + thread_progress->join(); + + std::cout << "Thread " << thread_progress->id() << ": finished." << std::endl; + + // Quit if it was the last thread to be joined. + if (std::find_if(progress_threads_.begin(), progress_threads_.end(), + std::mem_fun(&ThreadProgress::unfinished)) == progress_threads_.end()) + { + main_loop_->quit(); + } +} + +} // anonymous namespace + +int main(int, char**) +{ + Glib::init(); + + Application application; + application.run(); + + return 0; +}