diff options
Diffstat (limited to 'libs/glibmm2/glibmm/main.cc')
-rw-r--r-- | libs/glibmm2/glibmm/main.cc | 986 |
1 files changed, 986 insertions, 0 deletions
diff --git a/libs/glibmm2/glibmm/main.cc b/libs/glibmm2/glibmm/main.cc new file mode 100644 index 0000000000..45e1e0d684 --- /dev/null +++ b/libs/glibmm2/glibmm/main.cc @@ -0,0 +1,986 @@ +// -*- c++ -*- +/* $Id$ */ + +/* Copyright (C) 2002 The gtkmm Development Team + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <glibmm/main.h> +#include <glibmm/exceptionhandler.h> +#include <glibmm/thread.h> +#include <glibmm/wrap.h> +#include <glibmm/iochannel.h> + +#include <glib/gmessages.h> +#include <algorithm> + +GLIBMM_USING_STD(min) + + +namespace +{ + +class SourceConnectionNode +{ +public: + explicit inline SourceConnectionNode(const sigc::slot_base& slot); + + static void* notify(void* data); + static void destroy_notify_callback(void* data); + + inline void install(GSource* source); + inline sigc::slot_base* get_slot(); + +private: + sigc::slot_base slot_; + GSource* source_; +}; + +inline +SourceConnectionNode::SourceConnectionNode(const sigc::slot_base& slot) +: + slot_ (slot), + source_ (0) +{ + slot_.set_parent(this, &SourceConnectionNode::notify); +} + +void* SourceConnectionNode::notify(void* data) +{ + SourceConnectionNode *const self = static_cast<SourceConnectionNode*>(data); + + // if there is no object, this call was triggered from destroy_notify_handler(), + // because we set self->source_ to 0 there: + if (self->source_) + { + GSource* s = self->source_; + self->source_ = 0; + g_source_destroy(s); + + // Destroying the object triggers execution of destroy_notify_handler(), + // eiter immediately or later, so we leave that to do the deletion. + } + + return 0; +} + +// static +void SourceConnectionNode::destroy_notify_callback(void* data) +{ + SourceConnectionNode *const self = static_cast<SourceConnectionNode*>(data); + + if (self) + { + // The GLib side is disconnected now, thus the GSource* is no longer valid. + self->source_ = 0; + + delete self; + } +} + +inline +void SourceConnectionNode::install(GSource* source) +{ + source_ = source; +} + +inline +sigc::slot_base* SourceConnectionNode::get_slot() +{ + return &slot_; +} + + +/* We use the callback data member of GSource to store both a pointer to our + * wrapper and a pointer to the connection node that is currently being used. + * The one and only SourceCallbackData object of a Glib::Source is constructed + * in the ctor of Glib::Source and destroyed after the GSource object when the + * reference counter of the GSource object reaches zero! + */ +struct SourceCallbackData +{ + explicit inline SourceCallbackData(Glib::Source* wrapper_); + + void set_node(SourceConnectionNode* node_); + + static void destroy_notify_callback(void* data); + + Glib::Source* wrapper; + SourceConnectionNode* node; +}; + +inline +SourceCallbackData::SourceCallbackData(Glib::Source* wrapper_) +: + wrapper (wrapper_), + node (0) +{} + +void SourceCallbackData::set_node(SourceConnectionNode* node_) +{ + if(node) + SourceConnectionNode::destroy_notify_callback(node); + + node = node_; +} + +// static +void SourceCallbackData::destroy_notify_callback(void* data) +{ + SourceCallbackData *const self = static_cast<SourceCallbackData*>(data); + + if(self->node) + SourceConnectionNode::destroy_notify_callback(self->node); + + if(self->wrapper) + Glib::Source::destroy_notify_callback(self->wrapper); + + delete self; +} + + +/* Retrieve the callback data from a wrapped GSource object. + */ +static SourceCallbackData* glibmm_source_get_callback_data(GSource* source) +{ + g_return_val_if_fail(source->callback_funcs->get != 0, 0); + + GSourceFunc func; + void* user_data = 0; + + // Retrieve the callback function and data. + (*source->callback_funcs->get)(source->callback_data, source, &func, &user_data); + + return static_cast<SourceCallbackData*>(user_data); +} + +/* Glib::Source doesn't use the callback function installed with + * g_source_set_callback(). Instead, it invokes the sigc++ slot + * directly from dispatch_vfunc(), which is both simpler and more + * efficient. + * For correctness, provide a pointer to this dummy callback rather + * than some random pointer. That also allows for sanity checks + * here as well as in Source::dispatch_vfunc(). + */ +static gboolean glibmm_dummy_source_callback(void*) +{ + g_assert_not_reached(); + return 0; +} + +/* Only used by SignalTimeout::connect() and SignalIdle::connect(). + * These don't use Glib::Source, to avoid the unnecessary overhead + * of a completely unused wrapper object. + */ +static gboolean glibmm_source_callback(void* data) +{ + SourceConnectionNode *const conn_data = static_cast<SourceConnectionNode*>(data); + + try + { + // Recreate the specific slot from the generic slot node. + return (*static_cast<sigc::slot<bool>*>(conn_data->get_slot()))(); + } + catch(...) + { + Glib::exception_handlers_invoke(); + } + return 0; +} + +static gboolean glibmm_iosource_callback(GIOChannel*, GIOCondition condition, void* data) +{ + SourceCallbackData *const callback_data = static_cast<SourceCallbackData*>(data); + g_return_val_if_fail(callback_data->node != 0, 0); + + try + { + // Recreate the specific slot from the generic slot node. + return (*static_cast<sigc::slot<bool,Glib::IOCondition>*>(callback_data->node->get_slot())) + ((Glib::IOCondition) condition); + } + catch(...) + { + Glib::exception_handlers_invoke(); + } + return 0; +} + +} // anonymous namespace + + +namespace Glib +{ + +/**** Glib::PollFD *********************************************************/ + +PollFD::PollFD() +{ + gobject_.fd = 0; + gobject_.events = 0; + gobject_.revents = 0; +} + +PollFD::PollFD(int fd) +{ + gobject_.fd = fd; + gobject_.events = 0; + gobject_.revents = 0; +} + +PollFD::PollFD(int fd, IOCondition events) +{ + gobject_.fd = fd; + gobject_.events = events; + gobject_.revents = 0; +} + + +/**** Glib::SignalTimeout **************************************************/ + +inline +SignalTimeout::SignalTimeout(GMainContext* context) +: + context_ (context) +{} + +sigc::connection SignalTimeout::connect(const sigc::slot<bool>& slot, + unsigned int interval, int priority) +{ + SourceConnectionNode *const conn_node = new SourceConnectionNode(slot); + const sigc::connection connection (*conn_node->get_slot()); + + GSource *const source = g_timeout_source_new(interval); + + if(priority != G_PRIORITY_DEFAULT) + g_source_set_priority(source, priority); + + g_source_set_callback( + source, &glibmm_source_callback, conn_node, + &SourceConnectionNode::destroy_notify_callback); + + g_source_attach(source, context_); + g_source_unref(source); // GMainContext holds a reference + + conn_node->install(source); + return connection; +} + +SignalTimeout signal_timeout() +{ + return SignalTimeout(0); // 0 means default context +} + + +/**** Glib::SignalIdle *****************************************************/ + +inline +SignalIdle::SignalIdle(GMainContext* context) +: + context_ (context) +{} + +sigc::connection SignalIdle::connect(const sigc::slot<bool>& slot, int priority) +{ + SourceConnectionNode *const conn_node = new SourceConnectionNode(slot); + const sigc::connection connection (*conn_node->get_slot()); + + GSource *const source = g_idle_source_new(); + + if(priority != G_PRIORITY_DEFAULT) + g_source_set_priority(source, priority); + + g_source_set_callback( + source, &glibmm_source_callback, conn_node, + &SourceConnectionNode::destroy_notify_callback); + + g_source_attach(source, context_); + g_source_unref(source); // GMainContext holds a reference + + conn_node->install(source); + return connection; +} + +SignalIdle signal_idle() +{ + return SignalIdle(0); // 0 means default context +} + + +/**** Glib::SignalIO *******************************************************/ + +inline +SignalIO::SignalIO(GMainContext* context) +: + context_ (context) +{} + +sigc::connection SignalIO::connect(const sigc::slot<bool,IOCondition>& slot, + int fd, IOCondition condition, int priority) +{ + const Glib::RefPtr<IOSource> source = IOSource::create(fd, condition); + + if(priority != G_PRIORITY_DEFAULT) + source->set_priority(priority); + + const sigc::connection connection = source->connect(slot); + + g_source_attach(source->gobj(), context_); + + return connection; +} + +sigc::connection SignalIO::connect(const sigc::slot<bool,IOCondition>& slot, + const Glib::RefPtr<IOChannel>& channel, + IOCondition condition, int priority) +{ + const Glib::RefPtr<IOSource> source = IOSource::create(channel, condition); + + if(priority != G_PRIORITY_DEFAULT) + source->set_priority(priority); + + const sigc::connection connection = source->connect(slot); + + g_source_attach(source->gobj(), context_); + + return connection; +} + +SignalIO signal_io() +{ + return SignalIO(0); // 0 means default context +} + + +/**** Glib::MainContext ****************************************************/ + +// static +Glib::RefPtr<MainContext> MainContext::create() +{ + return Glib::RefPtr<MainContext>(reinterpret_cast<MainContext*>(g_main_context_new())); +} + +// static +Glib::RefPtr<MainContext> MainContext::get_default() +{ + return Glib::wrap(g_main_context_default(), true); +} + +bool MainContext::iteration(bool may_block) +{ + return g_main_context_iteration(gobj(), may_block); +} + +bool MainContext::pending() +{ + return g_main_context_pending(gobj()); +} + +void MainContext::wakeup() +{ + g_main_context_wakeup(gobj()); +} + +bool MainContext::acquire() +{ + return g_main_context_acquire(gobj()); +} + +bool MainContext::wait(Glib::Cond& cond, Glib::Mutex& mutex) +{ + return g_main_context_wait(gobj(), cond.gobj(), mutex.gobj()); +} + +void MainContext::release() +{ + g_main_context_release(gobj()); +} + +bool MainContext::prepare(int& priority) +{ + return g_main_context_prepare(gobj(), &priority); +} + +bool MainContext::prepare() +{ + return g_main_context_prepare(gobj(), 0); +} + +void MainContext::query(int max_priority, int& timeout, std::vector<PollFD>& fds) +{ + if(fds.empty()) + fds.resize(8); // rather bogus number, but better than 0 + + for(;;) + { + const int size_before = fds.size(); + const int size_needed = g_main_context_query( + gobj(), max_priority, &timeout, reinterpret_cast<GPollFD*>(&fds.front()), size_before); + + fds.resize(size_needed); + + if(size_needed <= size_before) + break; + } +} + +bool MainContext::check(int max_priority, std::vector<PollFD>& fds) +{ + if(!fds.empty()) + return g_main_context_check(gobj(), max_priority, reinterpret_cast<GPollFD*>(&fds.front()), fds.size()); + else + return false; +} + +void MainContext::dispatch() +{ + g_main_context_dispatch(gobj()); +} + +void MainContext::set_poll_func(GPollFunc poll_func) +{ + g_main_context_set_poll_func(gobj(), poll_func); +} + +GPollFunc MainContext::get_poll_func() +{ + return g_main_context_get_poll_func(gobj()); +} + +void MainContext::add_poll(PollFD& fd, int priority) +{ + g_main_context_add_poll(gobj(), fd.gobj(), priority); +} + +void MainContext::remove_poll(PollFD& fd) +{ + g_main_context_remove_poll(gobj(), fd.gobj()); +} + +SignalTimeout MainContext::signal_timeout() +{ + return SignalTimeout(gobj()); +} + +SignalIdle MainContext::signal_idle() +{ + return SignalIdle(gobj()); +} + +SignalIO MainContext::signal_io() +{ + return SignalIO(gobj()); +} + +void MainContext::reference() const +{ + g_main_context_ref(reinterpret_cast<GMainContext*>(const_cast<MainContext*>(this))); +} + +void MainContext::unreference() const +{ + g_main_context_unref(reinterpret_cast<GMainContext*>(const_cast<MainContext*>(this))); +} + +GMainContext* MainContext::gobj() +{ + return reinterpret_cast<GMainContext*>(this); +} + +const GMainContext* MainContext::gobj() const +{ + return reinterpret_cast<const GMainContext*>(this); +} + +GMainContext* MainContext::gobj_copy() const +{ + reference(); + return const_cast<GMainContext*>(gobj()); +} + +Glib::RefPtr<MainContext> wrap(GMainContext* gobject, bool take_copy) +{ + if(take_copy && gobject) + g_main_context_ref(gobject); + + return Glib::RefPtr<MainContext>(reinterpret_cast<MainContext*>(gobject)); +} + + +/**** Glib::MainLoop *******************************************************/ + +Glib::RefPtr<MainLoop> MainLoop::create(bool is_running) +{ + return Glib::RefPtr<MainLoop>( + reinterpret_cast<MainLoop*>(g_main_loop_new(0, is_running))); +} + +Glib::RefPtr<MainLoop> MainLoop::create(const Glib::RefPtr<MainContext>& context, bool is_running) +{ + return Glib::RefPtr<MainLoop>( + reinterpret_cast<MainLoop*>(g_main_loop_new(Glib::unwrap(context), is_running))); +} + +void MainLoop::run() +{ + g_main_loop_run(gobj()); +} + +void MainLoop::quit() +{ + g_main_loop_quit(gobj()); +} + +bool MainLoop::is_running() +{ + return g_main_loop_is_running(gobj()); +} + +Glib::RefPtr<MainContext> MainLoop::get_context() +{ + return Glib::wrap(g_main_loop_get_context(gobj()), true); +} + +//static: +int MainLoop::depth() +{ + return g_main_depth(); +} + +void MainLoop::reference() const +{ + g_main_loop_ref(reinterpret_cast<GMainLoop*>(const_cast<MainLoop*>(this))); +} + +void MainLoop::unreference() const +{ + g_main_loop_unref(reinterpret_cast<GMainLoop*>(const_cast<MainLoop*>(this))); +} + +GMainLoop* MainLoop::gobj() +{ + return reinterpret_cast<GMainLoop*>(this); +} + +const GMainLoop* MainLoop::gobj() const +{ + return reinterpret_cast<const GMainLoop*>(this); +} + +GMainLoop* MainLoop::gobj_copy() const +{ + reference(); + return const_cast<GMainLoop*>(gobj()); +} + +Glib::RefPtr<MainLoop> wrap(GMainLoop* gobject, bool take_copy) +{ + if(take_copy && gobject) + g_main_loop_ref(gobject); + + return Glib::RefPtr<MainLoop>(reinterpret_cast<MainLoop*>(gobject)); +} + + +/**** Glib::Source *********************************************************/ + +// static +const GSourceFuncs Source::vfunc_table_ = +{ + &Source::prepare_vfunc, + &Source::check_vfunc, + &Source::dispatch_vfunc, + 0, // finalize_vfunc // We can't use finalize_vfunc because there is no way + // to store a pointer to our wrapper anywhere in GSource so + // that it persists until finalize_vfunc would be called from here. + 0, // closure_callback + 0, // closure_marshal +}; + +unsigned int Source::attach(const Glib::RefPtr<MainContext>& context) +{ + return g_source_attach(gobject_, Glib::unwrap(context)); +} + +unsigned int Source::attach() +{ + return g_source_attach(gobject_, 0); +} + +void Source::destroy() +{ + g_source_destroy(gobject_); +} + +void Source::set_priority(int priority) +{ + g_source_set_priority(gobject_, priority); +} + +int Source::get_priority() const +{ + return g_source_get_priority(gobject_); +} + +void Source::set_can_recurse(bool can_recurse) +{ + g_source_set_can_recurse(gobject_, can_recurse); +} + +bool Source::get_can_recurse() const +{ + return g_source_get_can_recurse(gobject_); +} + +unsigned int Source::get_id() const +{ + return g_source_get_id(gobject_); +} + +Glib::RefPtr<MainContext> Source::get_context() +{ + return Glib::wrap(g_source_get_context(gobject_), true); +} + +GSource* Source::gobj_copy() const +{ + return g_source_ref(gobject_); +} + +void Source::reference() const +{ + g_source_ref(gobject_); +} + +void Source::unreference() const +{ + g_source_unref(gobject_); +} + +Source::Source() +: + gobject_ (g_source_new(const_cast<GSourceFuncs*>(&vfunc_table_), sizeof(GSource))) +{ + g_source_set_callback( + gobject_, &glibmm_dummy_source_callback, + new SourceCallbackData(this), // our persistant callback data object + &SourceCallbackData::destroy_notify_callback); +} + +Source::Source(GSource* cast_item, GSourceFunc callback_func) +: + gobject_ (cast_item) +{ + g_source_set_callback( + gobject_, callback_func, + new SourceCallbackData(this), // our persistant callback data object + &SourceCallbackData::destroy_notify_callback); +} + +Source::~Source() +{ + // The dtor should be invoked by destroy_notify_callback() only, which clears + // gobject_ before deleting. However, we might also get to this point if + // a derived ctor threw an exception, and then we need to unref manually. + + if(gobject_) + { + SourceCallbackData *const data = glibmm_source_get_callback_data(gobject_); + data->wrapper = 0; + + GSource *const tmp_gobject = gobject_; + gobject_ = 0; + + g_source_unref(tmp_gobject); + } +} + +sigc::connection Source::connect_generic(const sigc::slot_base& slot) +{ + SourceConnectionNode *const conn_node = new SourceConnectionNode(slot); + const sigc::connection connection (*conn_node->get_slot()); + + // Don't override the callback data. Reuse the existing one + // calling SourceCallbackData::set_node() to register conn_node. + SourceCallbackData *const data = glibmm_source_get_callback_data(gobject_); + data->set_node(conn_node); + + conn_node->install(gobject_); + return connection; +} + +void Source::add_poll(Glib::PollFD& poll_fd) +{ + g_source_add_poll(gobject_, poll_fd.gobj()); +} + +void Source::remove_poll(Glib::PollFD& poll_fd) +{ + g_source_remove_poll(gobject_, poll_fd.gobj()); +} + +void Source::get_current_time(Glib::TimeVal& current_time) +{ + g_source_get_current_time(gobject_, ¤t_time); +} + +inline // static +Source* Source::get_wrapper(GSource* source) +{ + SourceCallbackData *const data = glibmm_source_get_callback_data(source); + return data->wrapper; +} + +// static +gboolean Source::prepare_vfunc(GSource* source, int* timeout) +{ + try + { + Source *const self = get_wrapper(source); + return self->prepare(*timeout); + } + catch(...) + { + Glib::exception_handlers_invoke(); + } + return 0; +} + +// static +gboolean Source::check_vfunc(GSource* source) +{ + try + { + Source *const self = get_wrapper(source); + return self->check(); + } + catch(...) + { + Glib::exception_handlers_invoke(); + } + return 0; +} + +// static +gboolean Source::dispatch_vfunc(GSource*, GSourceFunc callback, void* user_data) +{ + SourceCallbackData *const callback_data = static_cast<SourceCallbackData*>(user_data); + + g_return_val_if_fail(callback == &glibmm_dummy_source_callback, 0); + g_return_val_if_fail(callback_data != 0 && callback_data->node != 0, 0); + + try + { + Source *const self = callback_data->wrapper; + return self->dispatch(callback_data->node->get_slot()); + } + catch(...) + { + Glib::exception_handlers_invoke(); + } + return 0; +} + +// static +void Source::destroy_notify_callback(void* data) +{ + if(data) + { + Source *const self = static_cast<Source*>(data); + + // gobject_ is already invalid at this point. + self->gobject_ = 0; + + // No exception checking: if the dtor throws, you're out of luck anyway. + delete self; + } +} + + +/**** Glib::TimeoutSource **************************************************/ + +// static +Glib::RefPtr<TimeoutSource> TimeoutSource::create(unsigned int interval) +{ + return Glib::RefPtr<TimeoutSource>(new TimeoutSource(interval)); +} + +sigc::connection TimeoutSource::connect(const sigc::slot<bool>& slot) +{ + return connect_generic(slot); +} + +TimeoutSource::TimeoutSource(unsigned int interval) +: + interval_ (interval) +{ + expiration_.assign_current_time(); + expiration_.add_milliseconds(std::min<unsigned long>(G_MAXLONG, interval_)); +} + +TimeoutSource::~TimeoutSource() +{} + +bool TimeoutSource::prepare(int& timeout) +{ + Glib::TimeVal current_time; + get_current_time(current_time); + + Glib::TimeVal remaining = expiration_; + remaining.subtract(current_time); + + if(remaining.negative()) + { + // Already expired. + timeout = 0; + } + else + { + const unsigned long milliseconds = + static_cast<unsigned long>(remaining.tv_sec) * 1000U + + static_cast<unsigned long>(remaining.tv_usec) / 1000U; + + // Set remaining milliseconds. + timeout = std::min<unsigned long>(G_MAXINT, milliseconds); + + // Check if the system time has been set backwards. (remaining > interval) + remaining.add_milliseconds(- std::min<unsigned long>(G_MAXLONG, interval_) - 1); + if(!remaining.negative()) + { + // Oh well. Reset the expiration time to now + interval; + // this at least avoids hanging for long periods of time. + expiration_ = current_time; + expiration_.add_milliseconds(interval_); + timeout = std::min<unsigned int>(G_MAXINT, interval_); + } + } + + return (timeout == 0); +} + +bool TimeoutSource::check() +{ + Glib::TimeVal current_time; + get_current_time(current_time); + + return (expiration_ <= current_time); +} + +bool TimeoutSource::dispatch(sigc::slot_base* slot) +{ + const bool again = (*static_cast<sigc::slot<bool>*>(slot))(); + + if(again) + { + get_current_time(expiration_); + expiration_.add_milliseconds(std::min<unsigned long>(G_MAXLONG, interval_)); + } + + return again; +} + + +/**** Glib::IdleSource *****************************************************/ + +// static +Glib::RefPtr<IdleSource> IdleSource::create() +{ + return Glib::RefPtr<IdleSource>(new IdleSource()); +} + +sigc::connection IdleSource::connect(const sigc::slot<bool>& slot) +{ + return connect_generic(slot); +} + +IdleSource::IdleSource() +{ + set_priority(PRIORITY_DEFAULT_IDLE); +} + +IdleSource::~IdleSource() +{} + +bool IdleSource::prepare(int& timeout) +{ + timeout = 0; + return true; +} + +bool IdleSource::check() +{ + return true; +} + +bool IdleSource::dispatch(sigc::slot_base* slot) +{ + return (*static_cast<sigc::slot<bool>*>(slot))(); +} + + +/**** Glib::IOSource *******************************************************/ + +// static +Glib::RefPtr<IOSource> IOSource::create(int fd, IOCondition condition) +{ + return Glib::RefPtr<IOSource>(new IOSource(fd, condition)); +} + +Glib::RefPtr<IOSource> IOSource::create(const Glib::RefPtr<IOChannel>& channel, IOCondition condition) +{ + return Glib::RefPtr<IOSource>(new IOSource(channel, condition)); +} + +sigc::connection IOSource::connect(const sigc::slot<bool,IOCondition>& slot) +{ + return connect_generic(slot); +} + +IOSource::IOSource(int fd, IOCondition condition) +: + poll_fd_ (fd, condition) +{ + add_poll(poll_fd_); +} + +IOSource::IOSource(const Glib::RefPtr<IOChannel>& channel, IOCondition condition) +: + Source(g_io_create_watch(channel->gobj(), (GIOCondition) condition), + (GSourceFunc) &glibmm_iosource_callback) +{} + +IOSource::~IOSource() +{} + +bool IOSource::prepare(int& timeout) +{ + timeout = -1; + return false; +} + +bool IOSource::check() +{ + return ((poll_fd_.get_revents() & poll_fd_.get_events()) != 0); +} + +bool IOSource::dispatch(sigc::slot_base* slot) +{ + return (*static_cast<sigc::slot<bool,IOCondition>*>(slot)) + (poll_fd_.get_revents()); +} + +} // namespace Glib + |