/* 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 Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include #include #include #include #include #include namespace { // Convert an interval from milliseconds to microseconds, // suitable for adding to Source::get_time(). inline gint64 ms2us(unsigned int ms) { return static_cast(ms) * 1000; } class SourceConnectionNode : public sigc::notifiable { public: explicit inline SourceConnectionNode(const sigc::slot_base& slot); static void notify(sigc::notifiable* data); static void destroy_notify_callback(sigc::notifiable* 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_(nullptr) { slot_.set_parent(this, &SourceConnectionNode::notify); } void SourceConnectionNode::notify(sigc::notifiable* data) { SourceConnectionNode* const self = static_cast(data); // if there is no object, this call was triggered from destroy_notify_handler(), // because we set self->source_ to nullptr there: if (self->source_) { GSource* s = self->source_; self->source_ = nullptr; g_source_destroy(s); // Destroying the object triggers execution of destroy_notify_handler(), // either immediately or later, so we leave that to do the deletion. } } // static void SourceConnectionNode::destroy_notify_callback(sigc::notifiable* data) { SourceConnectionNode* const self = static_cast(data); if (self) { // The GLib side is disconnected now, thus the GSource* is no longer valid. self->source_ = nullptr; 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 when the GSource object is destroyed, * which may occur before 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(nullptr) { } 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(data); if (self->node) SourceConnectionNode::destroy_notify_callback(self->node); // destroy_notify_callback2() does nothing if self->wrapper == nullptr. Glib::Source::destroy_notify_callback2(self->wrapper); delete self; } /* Retrieve the callback data from a wrapped GSource object. */ static SourceCallbackData* glibmm_source_get_callback_data(GSource* source) { /* There is race between iteration of sources in main loop * that checks whether they are still active and source * destruction happening in other threads. */ gpointer callback_data = source->callback_data; GSourceCallbackFuncs* callback_funcs = source->callback_funcs; g_return_val_if_fail(callback_funcs != nullptr, nullptr); if (g_source_is_destroyed(source)) return nullptr; GSourceFunc func; void* user_data = nullptr; // Retrieve the callback function and data. (*callback_funcs->get)(callback_data, source, &func, &user_data); return static_cast(user_data); } // The function pointers, glibmm_source_*_vfuncptr, are set in the Source ctor // that calls g_source_new(const_cast(&vfunc_table_), sizeof(GSource)), // making it possible to indirectly call the Source::*_vfunc() member functions // from the glibmm_source_*_callback() functions with C linkage. using SourcePrepareVFuncType = gboolean (*)(GSource* source, int* timeout); using SourceCheckVFuncType = gboolean (*)(GSource* source); using SourceDispatchVFuncType = gboolean (*)(GSource* source, GSourceFunc callback, void* user_data); SourcePrepareVFuncType glibmm_source_prepare_vfuncptr; SourceCheckVFuncType glibmm_source_check_vfuncptr; SourceDispatchVFuncType glibmm_source_dispatch_vfuncptr; extern "C" { static gboolean glibmm_source_prepare_callback(GSource* source, int* timeout) { return glibmm_source_prepare_vfuncptr(source, timeout); } static gboolean glibmm_source_check_callback(GSource* source) { return glibmm_source_check_vfuncptr(source); } static gboolean glibmm_source_dispatch_callback(GSource* source, GSourceFunc callback, void* user_data) { return glibmm_source_dispatch_vfuncptr(source, callback, 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(), SignalTimeout::connect_seconds() * 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(data); try { // Recreate the specific slot from the generic slot node. return (*static_cast*>(conn_data->get_slot()))(); } catch (...) { Glib::exception_handlers_invoke(); } return 0; } /* Only used by SignalTimeout::connect_once(), SignalTimeout::connect_seconds_once() * and SignalIdle::connect_once(). These don't use Glib::Source, to avoid the * unnecessary overhead of a completely unused wrapper object. */ static gboolean glibmm_source_callback_once(void* data) { SourceConnectionNode* const conn_data = static_cast(data); try { // Recreate the specific slot from the generic slot node. (*static_cast*>(conn_data->get_slot()))(); } catch (...) { Glib::exception_handlers_invoke(); } return 0; // Destroy the event source after one call } static void glibmm_source_destroy_notify_callback(void* data) { SourceConnectionNode* const conn_data = static_cast(data); SourceConnectionNode::destroy_notify_callback(conn_data); } static gboolean glibmm_iosource_callback(GIOChannel*, GIOCondition condition, void* data) { SourceCallbackData* const callback_data = static_cast(data); g_return_val_if_fail(callback_data->node != nullptr, 0); try { // Recreate the specific slot from the generic slot node. return (*static_cast*>(callback_data->node->get_slot()))( (Glib::IOCondition)condition); } catch (...) { Glib::exception_handlers_invoke(); } return 0; } /* Only used by SignalChildWatch::connect(). * These don't use Glib::Source, to avoid the unnecessary overhead * of a completely unused wrapper object. */ static gboolean glibmm_child_watch_callback(GPid pid, gint child_status, void* data) { SourceConnectionNode* const conn_data = static_cast(data); try { // Recreate the specific slot from the generic slot node. (*static_cast*>(conn_data->get_slot()))(pid, child_status); } catch (...) { Glib::exception_handlers_invoke(); } return 0; } } // extern "C" static void glibmm_signal_connect_once( const sigc::slot& slot, int priority, GSource* source, GMainContext* context) { SourceConnectionNode* const conn_node = new SourceConnectionNode(slot); if (priority != G_PRIORITY_DEFAULT) g_source_set_priority(source, priority); g_source_set_callback(source, &glibmm_source_callback_once, conn_node, &glibmm_source_destroy_notify_callback); conn_node->install(source); g_source_attach(source, context); g_source_unref(source); // GMainContext holds a reference } extern "C" { static gboolean glibmm_main_context_invoke_callback(void* data) { sigc::slot_base* const slot = reinterpret_cast(data); try { // Recreate the specific slot from the generic slot node. return (*static_cast*>(slot))(); } catch (...) { Glib::exception_handlers_invoke(); } return 0; } static void glibmm_main_context_invoke_destroy_notify_callback(void* data) { sigc::slot_base* const slot = reinterpret_cast(data); delete slot; } static void glibmm_source_callback_data_destroy_notify_callback(void* data) { SourceCallbackData::destroy_notify_callback(data); } } // extern "C" } // anonymous namespace namespace Glib { /**** Glib::PollFD *********************************************************/ PollFD::PollFD() { gobject_.fd = 0; gobject_.events = 0; gobject_.revents = 0; } PollFD::PollFD(PollFD::fd_t fd) { gobject_.fd = fd; gobject_.events = 0; gobject_.revents = 0; } PollFD::PollFD(PollFD::fd_t fd, IOCondition events) { gobject_.fd = fd; gobject_.events = static_cast(events); gobject_.revents = 0; } /**** Glib::SignalTimeout **************************************************/ inline SignalTimeout::SignalTimeout(GMainContext* context) : context_(context) { } /* Note that this is our equivalent of g_timeout_add(). */ sigc::connection SignalTimeout::connect(const sigc::slot& 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, &glibmm_source_destroy_notify_callback); conn_node->install(source); g_source_attach(source, context_); g_source_unref(source); // GMainContext holds a reference return connection; } void SignalTimeout::connect_once(const sigc::slot& slot, unsigned int interval, int priority) { GSource* const source = g_timeout_source_new(interval); glibmm_signal_connect_once(slot, priority, source, context_); } /* Note that this is our equivalent of g_timeout_add_seconds(). */ sigc::connection SignalTimeout::connect_seconds(const sigc::slot& 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_seconds(interval); if (priority != G_PRIORITY_DEFAULT) g_source_set_priority(source, priority); g_source_set_callback( source, &glibmm_source_callback, conn_node, &glibmm_source_destroy_notify_callback); conn_node->install(source); g_source_attach(source, context_); g_source_unref(source); // GMainContext holds a reference return connection; } void SignalTimeout::connect_seconds_once( const sigc::slot& slot, unsigned int interval, int priority) { GSource* const source = g_timeout_source_new_seconds(interval); glibmm_signal_connect_once(slot, priority, source, context_); } SignalTimeout signal_timeout() { return SignalTimeout(nullptr); // nullptr means default context } /**** Glib::SignalIdle *****************************************************/ inline SignalIdle::SignalIdle(GMainContext* context) : context_(context) { } sigc::connection SignalIdle::connect(const sigc::slot& 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, &glibmm_source_destroy_notify_callback); conn_node->install(source); g_source_attach(source, context_); g_source_unref(source); // GMainContext holds a reference return connection; } void SignalIdle::connect_once(const sigc::slot& slot, int priority) { GSource* const source = g_idle_source_new(); glibmm_signal_connect_once(slot, priority, source, context_); } SignalIdle signal_idle() { return SignalIdle(nullptr); // nullptr means default context } /**** Glib::SignalIO *******************************************************/ inline SignalIO::SignalIO(GMainContext* context) : context_(context) { } sigc::connection SignalIO::connect( const sigc::slot& slot, PollFD::fd_t fd, IOCondition condition, int priority) { const auto 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& slot, const Glib::RefPtr& channel, IOCondition condition, int priority) { const auto 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(nullptr); // nullptr means default context } /**** Glib::SignalChildWatch **************************************************/ inline SignalChildWatch::SignalChildWatch(GMainContext* context) : context_(context) { } sigc::connection SignalChildWatch::connect(const sigc::slot& slot, GPid pid, int priority) { SourceConnectionNode* const conn_node = new SourceConnectionNode(slot); const sigc::connection connection(*conn_node->get_slot()); GSource* const source = g_child_watch_source_new(pid); if (priority != G_PRIORITY_DEFAULT) g_source_set_priority(source, priority); g_source_set_callback(source, Glib::function_pointer_cast(&glibmm_child_watch_callback), conn_node, &glibmm_source_destroy_notify_callback); conn_node->install(source); g_source_attach(source, context_); g_source_unref(source); // GMainContext holds a reference return connection; } SignalChildWatch signal_child_watch() { return SignalChildWatch(nullptr); // nullptr means default context } /**** Glib::MainContext ****************************************************/ // static Glib::RefPtr MainContext::create() { return Glib::make_refptr_for_instance(reinterpret_cast(g_main_context_new())); } // static Glib::RefPtr MainContext::create(MainContextFlags flags) { return Glib::make_refptr_for_instance( reinterpret_cast(g_main_context_new_with_flags(static_cast(flags)))); } // static Glib::RefPtr 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()); } 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(), nullptr); } void MainContext::query(int max_priority, int& timeout, std::vector& 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(&fds.front()), size_before); fds.resize(size_needed); if (size_needed <= size_before) break; } } bool MainContext::check(int max_priority, std::vector& fds) { if (!fds.empty()) return g_main_context_check( gobj(), max_priority, reinterpret_cast(&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()); } void MainContext::push_thread_default() { g_main_context_push_thread_default(gobj()); } void MainContext::pop_thread_default() { g_main_context_pop_thread_default(gobj()); } // static Glib::RefPtr MainContext::get_thread_default() { // g_main_context_ref_thread_default() gives us a ref. return Glib::wrap(g_main_context_ref_thread_default(), false); } void MainContext::invoke(const sigc::slot& slot, int priority) { // Make a copy of slot on the heap. sigc::slot_base* const slot_copy = new sigc::slot(slot); g_main_context_invoke_full(gobj(), priority, glibmm_main_context_invoke_callback, slot_copy, glibmm_main_context_invoke_destroy_notify_callback); } SignalTimeout MainContext::signal_timeout() { return SignalTimeout(gobj()); } SignalIdle MainContext::signal_idle() { return SignalIdle(gobj()); } SignalIO MainContext::signal_io() { return SignalIO(gobj()); } SignalChildWatch MainContext::signal_child_watch() { return SignalChildWatch(gobj()); } void MainContext::reference() const { g_main_context_ref(reinterpret_cast(const_cast(this))); } void MainContext::unreference() const { g_main_context_unref(reinterpret_cast(const_cast(this))); } GMainContext* MainContext::gobj() { return reinterpret_cast(this); } const GMainContext* MainContext::gobj() const { return reinterpret_cast(this); } GMainContext* MainContext::gobj_copy() const { reference(); return const_cast(gobj()); } Glib::RefPtr wrap(GMainContext* gobject, bool take_copy) { if (take_copy && gobject) g_main_context_ref(gobject); return Glib::make_refptr_for_instance(reinterpret_cast(gobject)); } /**** Glib::MainLoop *******************************************************/ Glib::RefPtr MainLoop::create(bool is_running) { return Glib::make_refptr_for_instance(reinterpret_cast(g_main_loop_new(nullptr, is_running))); } Glib::RefPtr MainLoop::create(const Glib::RefPtr& context, bool is_running) { return Glib::make_refptr_for_instance( reinterpret_cast(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 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(const_cast(this))); } void MainLoop::unreference() const { g_main_loop_unref(reinterpret_cast(const_cast(this))); } GMainLoop* MainLoop::gobj() { return reinterpret_cast(this); } const GMainLoop* MainLoop::gobj() const { return reinterpret_cast(this); } GMainLoop* MainLoop::gobj_copy() const { reference(); return const_cast(gobj()); } Glib::RefPtr wrap(GMainLoop* gobject, bool take_copy) { if (take_copy && gobject) g_main_loop_ref(gobject); return Glib::make_refptr_for_instance(reinterpret_cast(gobject)); } /**** Glib::Source *********************************************************/ // static const GSourceFuncs Source::vfunc_table_ = { &glibmm_source_prepare_callback, &glibmm_source_check_callback, &glibmm_source_dispatch_callback, // 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. nullptr, // finalize_vfunc nullptr, // closure_callback nullptr, // closure_marshal }; unsigned int Source::attach(const Glib::RefPtr& context) { return g_source_attach(gobject_, Glib::unwrap(context)); } unsigned int Source::attach() { return g_source_attach(gobject_, nullptr); } 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 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 { ++ref_count_; } void Source::unreference() const { if (--ref_count_ == 0) { GSource* const tmp_gobject = gobject_; destroy_notify_callback2(const_cast(this)); // Drop the one and only GSource reference held by the C++ wrapper. // If the GSource instance is attached to a main context, the GMainContext // holds a reference until the source is detached (destroyed). g_source_unref(tmp_gobject); } } Source::Source() : gobject_(g_source_new(const_cast(&vfunc_table_), sizeof(GSource))) { glibmm_source_prepare_vfuncptr = &prepare_vfunc; glibmm_source_check_vfuncptr = &check_vfunc; glibmm_source_dispatch_vfuncptr = &dispatch_vfunc; g_source_set_callback(gobject_, &glibmm_dummy_source_callback, new SourceCallbackData(this), // our persistent callback data object &glibmm_source_callback_data_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 persistent callback data object &glibmm_source_callback_data_destroy_notify_callback); } Source::~Source() noexcept { // 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_); if (data) { data->wrapper = nullptr; } GSource* const tmp_gobject = gobject_; gobject_ = nullptr; g_source_unref(tmp_gobject); // The constructor does not add this to extra_source_data. No need to erase. } } 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_); if (data) { 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()); } gint64 Source::get_time() const { if (g_source_get_context(const_cast(gobject_))) return g_source_get_time(const_cast(gobject_)); else return g_get_monotonic_time(); } inline // static Source* Source::get_wrapper(GSource* source) { SourceCallbackData* const data = glibmm_source_get_callback_data(source); return (data) ? data->wrapper : nullptr; } // static gboolean Source::prepare_vfunc(GSource* source, int* timeout) { try { Source* const self = get_wrapper(source); return (self) ? self->prepare(*timeout) : 0; } catch (...) { Glib::exception_handlers_invoke(); } return 0; } // static gboolean Source::check_vfunc(GSource* source) { try { Source* const self = get_wrapper(source); return (self) ? self->check() : 0; } catch (...) { Glib::exception_handlers_invoke(); } return 0; } // static gboolean Source::dispatch_vfunc(GSource*, GSourceFunc callback, void* user_data) { SourceCallbackData* const callback_data = static_cast(user_data); g_return_val_if_fail(callback == &glibmm_dummy_source_callback, 0); g_return_val_if_fail(callback_data != nullptr && callback_data->node != nullptr, 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_callback2(void* data) { if (data) { Source* const self = static_cast(data); if (--self->keep_wrapper_ == 0) { // gobject_ is already invalid at this point. self->gobject_ = nullptr; // No exception checking: if the dtor throws, you're out of luck anyway. delete self; } } } // static sigc::connection Source::attach_signal_source(const sigc::slot_base& slot, int priority, GSource* source, GMainContext* context, GSourceFunc callback_func) { SourceConnectionNode* const conn_node = new SourceConnectionNode(slot); const sigc::connection connection(*conn_node->get_slot()); if (priority != G_PRIORITY_DEFAULT) g_source_set_priority(source, priority); g_source_set_callback( source, callback_func, conn_node, &glibmm_source_destroy_notify_callback); conn_node->install(source); g_source_attach(source, context); g_source_unref(source); // GMainContext holds a reference return connection; } // static sigc::slot_base* Source::get_slot_from_connection_node(void* data) { return static_cast(data)->get_slot(); } // static sigc::slot_base* Source::get_slot_from_callback_data(void* data) { SourceCallbackData* const callback_data = static_cast(data); g_return_val_if_fail(callback_data->node != nullptr, nullptr); return callback_data->node->get_slot(); } /**** Glib::TimeoutSource **************************************************/ // static Glib::RefPtr TimeoutSource::create(unsigned int interval) { return Glib::make_refptr_for_instance(new TimeoutSource(interval)); } sigc::connection TimeoutSource::connect(const sigc::slot& slot) { return connect_generic(slot); } TimeoutSource::TimeoutSource(unsigned int interval) : interval_(interval) { expiration_ = get_time() + ms2us(interval_); } TimeoutSource::~TimeoutSource() noexcept { } bool TimeoutSource::prepare(int& timeout) { gint64 remaining = expiration_ - get_time(); if (remaining <= 0) { // Already expired. timeout = 0; } else { // Set remaining milliseconds. timeout = std::min(G_MAXINT, remaining / 1000); // Check if the system time has been set backwards. (remaining > interval) if (remaining > ms2us(interval_)) { // Oh well. Reset the expiration time to now + interval; // this at least avoids hanging for long periods of time. expiration_ = get_time() + ms2us(interval_); timeout = std::min(G_MAXINT, interval_); } } return (timeout == 0); } bool TimeoutSource::check() { return expiration_ <= get_time(); } bool TimeoutSource::dispatch(sigc::slot_base* slot) { const bool again = (*static_cast*>(slot))(); if (again) expiration_ = get_time() + ms2us(interval_); return again; } /**** Glib::IdleSource *****************************************************/ // static Glib::RefPtr IdleSource::create() { return Glib::make_refptr_for_instance(new IdleSource()); } sigc::connection IdleSource::connect(const sigc::slot& slot) { return connect_generic(slot); } IdleSource::IdleSource() { set_priority(PRIORITY_DEFAULT_IDLE); } IdleSource::~IdleSource() noexcept { } bool IdleSource::prepare(int& timeout) { timeout = 0; return true; } bool IdleSource::check() { return true; } bool IdleSource::dispatch(sigc::slot_base* slot) { return (*static_cast*>(slot))(); } /**** Glib::IOSource *******************************************************/ // static Glib::RefPtr IOSource::create(PollFD::fd_t fd, IOCondition condition) { return Glib::make_refptr_for_instance(new IOSource(fd, condition)); } Glib::RefPtr IOSource::create(const Glib::RefPtr& channel, IOCondition condition) { return Glib::make_refptr_for_instance(new IOSource(channel, condition)); } Glib::RefPtr IOSource::create(GIOChannel* channel, IOCondition condition) { return Glib::make_refptr_for_instance(new IOSource(channel, condition)); } sigc::connection IOSource::connect(const sigc::slot& slot) { return connect_generic(slot); } IOSource::IOSource(PollFD::fd_t fd, IOCondition condition) : poll_fd_(fd, condition) { add_poll(poll_fd_); } IOSource::IOSource(const Glib::RefPtr& channel, IOCondition condition) : Source(g_io_create_watch(channel->gobj(), (GIOCondition)condition), Glib::function_pointer_cast(&glibmm_iosource_callback)) { } IOSource::IOSource(GIOChannel* channel, IOCondition condition) : Source(g_io_create_watch(channel, (GIOCondition)condition), Glib::function_pointer_cast(&glibmm_iosource_callback)) { } IOSource::IOSource(GSource* cast_item, GSourceFunc callback_func) : Source(cast_item, callback_func) { } IOSource::~IOSource() noexcept { } bool IOSource::prepare(int& timeout) { timeout = -1; return false; } bool IOSource::check() { return static_cast(poll_fd_.get_revents() & poll_fd_.get_events()) != 0; } bool IOSource::dispatch(sigc::slot_base* slot) { return (*static_cast*>(slot))(poll_fd_.get_revents()); } } // namespace Glib