1 /* Copyright (C) 2002 The gtkmm Development Team
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2.1 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <glibmmconfig.h> // May define GLIBMM_DISABLE_DEPRECATED
18 
19 #ifndef GLIBMM_DISABLE_DEPRECATED
20 // Include glibmm/thread.h first because we need it to be first to include <glib.h>,
21 // so we can do an undef trick to still use deprecated API in the header:
22 #include <glibmm/thread.h>
23 #include <glibmm/threads.h>
24 #endif // GLIBMM_DISABLE_DEPRECATED
25 
26 #include <glibmm/main.h>
27 #include <glibmm/exceptionhandler.h>
28 #include <glibmm/wrap.h>
29 #include <glibmm/iochannel.h>
30 #include <glibmm/utility.h>
31 #include <algorithm>
32 #include <map> // Needed until the next ABI break.
33 
34 namespace
35 {
36 void
time64_to_time_val(gint64 time64,Glib::TimeVal & time_val)37 time64_to_time_val(gint64 time64, Glib::TimeVal& time_val)
38 {
39   // This function is not guaranteed to convert correctly if time64 is negative.
40   const long seconds = static_cast<long>(time64 / G_GINT64_CONSTANT(1000000));
41   const long microseconds =
42     static_cast<long>(time64 - static_cast<gint64>(seconds) * G_GINT64_CONSTANT(1000000));
43   time_val = Glib::TimeVal(seconds, microseconds);
44 }
45 
46 // TODO: At the next ABI break, replace ExtraSourceData by new data members in Source.
47 // Then the mutex is not necessary, but to keep the code thread-safe, use the
48 // g_atomic_*() functions on these data elements.
49 // These are new data members that can't be added to Glib::Source now,
50 // because it would break ABI.
51 struct ExtraSourceData
52 {
ExtraSourceData__anon93432e3b0111::ExtraSourceData53   ExtraSourceData() : ref_count(1), keep_wrapper(2) {}
54   int ref_count;
55   // When both Source::unreference() and SourceCallbackData::destroy_notify_callback()
56   // have decreased keep_wrapper, it's time to delete the C++ wrapper.
57   int keep_wrapper;
58 };
59 
60 std::map<const Glib::Source*, ExtraSourceData> extra_source_data;
61 // Source instances may be used in different threads.
62 // Accesses to extra_source_data must be thread-safe.
63 std::mutex extra_source_data_mutex;
64 
65 class SourceConnectionNode
66 {
67 public:
68   explicit inline SourceConnectionNode(const sigc::slot_base& slot);
69 
70   static void* notify(void* data);
71   static void destroy_notify_callback(void* data);
72 
73   inline void install(GSource* source);
74   inline sigc::slot_base* get_slot();
75 
76 private:
77   sigc::slot_base slot_;
78   GSource* source_;
79 };
80 
SourceConnectionNode(const sigc::slot_base & slot)81 inline SourceConnectionNode::SourceConnectionNode(const sigc::slot_base& slot)
82 : slot_(slot), source_(nullptr)
83 {
84   slot_.set_parent(this, &SourceConnectionNode::notify);
85 }
86 
87 void*
notify(void * data)88 SourceConnectionNode::notify(void* data)
89 {
90   SourceConnectionNode* const self = static_cast<SourceConnectionNode*>(data);
91 
92   // if there is no object, this call was triggered from destroy_notify_handler(),
93   // because we set self->source_ to nullptr there:
94   if (self->source_)
95   {
96     GSource* s = self->source_;
97     self->source_ = nullptr;
98     g_source_destroy(s);
99 
100     // Destroying the object triggers execution of destroy_notify_handler(),
101     // either immediately or later, so we leave that to do the deletion.
102   }
103 
104   return nullptr;
105 }
106 
107 // static
108 void
destroy_notify_callback(void * data)109 SourceConnectionNode::destroy_notify_callback(void* data)
110 {
111   SourceConnectionNode* const self = static_cast<SourceConnectionNode*>(data);
112 
113   if (self)
114   {
115     // The GLib side is disconnected now, thus the GSource* is no longer valid.
116     self->source_ = nullptr;
117 
118     delete self;
119   }
120 }
121 
122 inline void
install(GSource * source)123 SourceConnectionNode::install(GSource* source)
124 {
125   source_ = source;
126 }
127 
128 inline sigc::slot_base*
get_slot()129 SourceConnectionNode::get_slot()
130 {
131   return &slot_;
132 }
133 
134 /* We use the callback data member of GSource to store both a pointer to our
135  * wrapper and a pointer to the connection node that is currently being used.
136  * The one and only SourceCallbackData object of a Glib::Source is constructed
137  * in the ctor of Glib::Source and destroyed when the GSource object is destroyed,
138  * which may occur before the reference counter of the GSource object reaches zero!
139  */
140 struct SourceCallbackData
141 {
142   explicit inline SourceCallbackData(Glib::Source* wrapper_);
143 
144   void set_node(SourceConnectionNode* node_);
145 
146   static void destroy_notify_callback(void* data);
147 
148   Glib::Source* wrapper;
149   SourceConnectionNode* node;
150 };
151 
SourceCallbackData(Glib::Source * wrapper_)152 inline SourceCallbackData::SourceCallbackData(Glib::Source* wrapper_)
153 : wrapper(wrapper_), node(nullptr)
154 {
155 }
156 
157 void
set_node(SourceConnectionNode * node_)158 SourceCallbackData::set_node(SourceConnectionNode* node_)
159 {
160   if (node)
161     SourceConnectionNode::destroy_notify_callback(node);
162 
163   node = node_;
164 }
165 
166 // static
167 void
destroy_notify_callback(void * data)168 SourceCallbackData::destroy_notify_callback(void* data)
169 {
170   SourceCallbackData* const self = static_cast<SourceCallbackData*>(data);
171 
172   if (self->node)
173     SourceConnectionNode::destroy_notify_callback(self->node);
174 
175   if (self->wrapper)
176   {
177     std::unique_lock<std::mutex> lock(extra_source_data_mutex);
178     if (--extra_source_data[self->wrapper].keep_wrapper == 0)
179     {
180       // No other reference exists to the wrapper. Delete it!
181       extra_source_data.erase(self->wrapper);
182       lock.unlock();
183       Glib::Source::destroy_notify_callback(self->wrapper);
184     }
185   }
186 
187   delete self;
188 }
189 
190 /* Retrieve the callback data from a wrapped GSource object.
191  */
192 static SourceCallbackData*
glibmm_source_get_callback_data(GSource * source)193 glibmm_source_get_callback_data(GSource* source)
194 {
195   /* There is race between iteration of sources in main loop
196    * that checks whether they are still active and source
197    * destruction happening in other threads.
198    */
199   gpointer callback_data = source->callback_data;
200   GSourceCallbackFuncs* callback_funcs = source->callback_funcs;
201 
202   g_return_val_if_fail(callback_funcs != nullptr, nullptr);
203 
204   if (g_source_is_destroyed(source))
205     return nullptr;
206 
207   GSourceFunc func;
208   void* user_data = nullptr;
209 
210   // Retrieve the callback function and data.
211   (*callback_funcs->get)(callback_data, source, &func, &user_data);
212 
213   return static_cast<SourceCallbackData*>(user_data);
214 }
215 
216 /* Glib::Source doesn't use the callback function installed with
217  * g_source_set_callback().  Instead, it invokes the sigc++ slot
218  * directly from dispatch_vfunc(), which is both simpler and more
219  * efficient.
220  * For correctness, provide a pointer to this dummy callback rather
221  * than some random pointer.  That also allows for sanity checks
222  * here as well as in Source::dispatch_vfunc().
223  */
224 static gboolean
glibmm_dummy_source_callback(void *)225 glibmm_dummy_source_callback(void*)
226 {
227   g_assert_not_reached();
228   return 0;
229 }
230 
231 /* Only used by SignalTimeout::connect(), SignalTimeout::connect_seconds()
232  * and SignalIdle::connect(). These don't use Glib::Source, to avoid the
233  * unnecessary overhead of a completely unused wrapper object.
234  */
235 static gboolean
glibmm_source_callback(void * data)236 glibmm_source_callback(void* data)
237 {
238   SourceConnectionNode* const conn_data = static_cast<SourceConnectionNode*>(data);
239 
240   try
241   {
242     // Recreate the specific slot from the generic slot node.
243     return (*static_cast<sigc::slot<bool>*>(conn_data->get_slot()))();
244   }
245   catch (...)
246   {
247     Glib::exception_handlers_invoke();
248   }
249   return 0;
250 }
251 
252 /* Only used by SignalTimeout::connect_once(), SignalTimeout::connect_seconds_once()
253  * and SignalIdle::connect_once(). These don't use Glib::Source, to avoid the
254  * unnecessary overhead of a completely unused wrapper object.
255  */
256 static gboolean
glibmm_source_callback_once(void * data)257 glibmm_source_callback_once(void* data)
258 {
259   SourceConnectionNode* const conn_data = static_cast<SourceConnectionNode*>(data);
260 
261   try
262   {
263     // Recreate the specific slot from the generic slot node.
264     (*static_cast<sigc::slot<void>*>(conn_data->get_slot()))();
265   }
266   catch (...)
267   {
268     Glib::exception_handlers_invoke();
269   }
270   return 0; // Destroy the event source after one call
271 }
272 
273 static gboolean
glibmm_iosource_callback(GIOChannel *,GIOCondition condition,void * data)274 glibmm_iosource_callback(GIOChannel*, GIOCondition condition, void* data)
275 {
276   SourceCallbackData* const callback_data = static_cast<SourceCallbackData*>(data);
277   g_return_val_if_fail(callback_data->node != nullptr, 0);
278 
279   try
280   {
281     // Recreate the specific slot from the generic slot node.
282     return (*static_cast<sigc::slot<bool, Glib::IOCondition>*>(callback_data->node->get_slot()))(
283       (Glib::IOCondition)condition);
284   }
285   catch (...)
286   {
287     Glib::exception_handlers_invoke();
288   }
289   return 0;
290 }
291 
292 /* Only used by SignalChildWatch::connect().
293  * These don't use Glib::Source, to avoid the unnecessary overhead
294  * of a completely unused wrapper object.
295  */
296 static gboolean
glibmm_child_watch_callback(GPid pid,gint child_status,void * data)297 glibmm_child_watch_callback(GPid pid, gint child_status, void* data)
298 {
299   SourceConnectionNode* const conn_data = static_cast<SourceConnectionNode*>(data);
300 
301   try
302   {
303     // Recreate the specific slot from the generic slot node.
304     (*static_cast<sigc::slot<void, GPid, int>*>(conn_data->get_slot()))(pid, child_status);
305   }
306   catch (...)
307   {
308     Glib::exception_handlers_invoke();
309   }
310   return 0;
311 }
312 
313 static void
glibmm_signal_connect_once(const sigc::slot<void> & slot,int priority,GSource * source,GMainContext * context)314 glibmm_signal_connect_once(
315   const sigc::slot<void>& slot, int priority, GSource* source, GMainContext* context)
316 {
317   SourceConnectionNode* const conn_node = new SourceConnectionNode(slot);
318 
319   if (priority != G_PRIORITY_DEFAULT)
320     g_source_set_priority(source, priority);
321 
322   g_source_set_callback(source, &glibmm_source_callback_once, conn_node,
323     &SourceConnectionNode::destroy_notify_callback);
324 
325   conn_node->install(source);
326   g_source_attach(source, context);
327   g_source_unref(source); // GMainContext holds a reference
328 }
329 
330 gboolean
glibmm_main_context_invoke_callback(void * data)331 glibmm_main_context_invoke_callback(void* data)
332 {
333   sigc::slot_base* const slot = reinterpret_cast<sigc::slot_base*>(data);
334 
335   try
336   {
337     // Recreate the specific slot from the generic slot node.
338     return (*static_cast<sigc::slot<bool>*>(slot))();
339   }
340   catch (...)
341   {
342     Glib::exception_handlers_invoke();
343   }
344   return 0;
345 }
346 
347 void
glibmm_main_context_invoke_destroy_notify_callback(void * data)348 glibmm_main_context_invoke_destroy_notify_callback(void* data)
349 {
350   sigc::slot_base* const slot = reinterpret_cast<sigc::slot_base*>(data);
351   delete slot;
352 }
353 
354 } // anonymous namespace
355 
356 namespace Glib
357 {
358 
359 /**** Glib::PollFD *********************************************************/
360 
PollFD()361 PollFD::PollFD()
362 {
363   gobject_.fd = 0;
364   gobject_.events = 0;
365   gobject_.revents = 0;
366 }
367 
PollFD(PollFD::fd_t fd)368 PollFD::PollFD(PollFD::fd_t fd)
369 {
370   gobject_.fd = fd;
371   gobject_.events = 0;
372   gobject_.revents = 0;
373 }
374 
PollFD(PollFD::fd_t fd,IOCondition events)375 PollFD::PollFD(PollFD::fd_t fd, IOCondition events)
376 {
377   gobject_.fd = fd;
378   gobject_.events = events;
379   gobject_.revents = 0;
380 }
381 
382 /**** Glib::SignalTimeout **************************************************/
383 
SignalTimeout(GMainContext * context)384 inline SignalTimeout::SignalTimeout(GMainContext* context) : context_(context)
385 {
386 }
387 
388 /* Note that this is our equivalent of g_timeout_add(). */
389 sigc::connection
connect(const sigc::slot<bool> & slot,unsigned int interval,int priority)390 SignalTimeout::connect(const sigc::slot<bool>& slot, unsigned int interval, int priority)
391 {
392   SourceConnectionNode* const conn_node = new SourceConnectionNode(slot);
393   const sigc::connection connection(*conn_node->get_slot());
394 
395   GSource* const source = g_timeout_source_new(interval);
396 
397   if (priority != G_PRIORITY_DEFAULT)
398     g_source_set_priority(source, priority);
399 
400   g_source_set_callback(
401     source, &glibmm_source_callback, conn_node, &SourceConnectionNode::destroy_notify_callback);
402 
403   conn_node->install(source);
404   g_source_attach(source, context_);
405   g_source_unref(source); // GMainContext holds a reference
406 
407   return connection;
408 }
409 
410 void
connect_once(const sigc::slot<void> & slot,unsigned int interval,int priority)411 SignalTimeout::connect_once(const sigc::slot<void>& slot, unsigned int interval, int priority)
412 {
413   GSource* const source = g_timeout_source_new(interval);
414   glibmm_signal_connect_once(slot, priority, source, context_);
415 }
416 
417 /* Note that this is our equivalent of g_timeout_add_seconds(). */
418 sigc::connection
connect_seconds(const sigc::slot<bool> & slot,unsigned int interval,int priority)419 SignalTimeout::connect_seconds(const sigc::slot<bool>& slot, unsigned int interval, int priority)
420 {
421   SourceConnectionNode* const conn_node = new SourceConnectionNode(slot);
422   const sigc::connection connection(*conn_node->get_slot());
423 
424   GSource* const source = g_timeout_source_new_seconds(interval);
425 
426   if (priority != G_PRIORITY_DEFAULT)
427     g_source_set_priority(source, priority);
428 
429   g_source_set_callback(
430     source, &glibmm_source_callback, conn_node, &SourceConnectionNode::destroy_notify_callback);
431 
432   conn_node->install(source);
433   g_source_attach(source, context_);
434   g_source_unref(source); // GMainContext holds a reference
435 
436   return connection;
437 }
438 
439 void
connect_seconds_once(const sigc::slot<void> & slot,unsigned int interval,int priority)440 SignalTimeout::connect_seconds_once(
441   const sigc::slot<void>& slot, unsigned int interval, int priority)
442 {
443   GSource* const source = g_timeout_source_new_seconds(interval);
444   glibmm_signal_connect_once(slot, priority, source, context_);
445 }
446 
447 SignalTimeout
signal_timeout()448 signal_timeout()
449 {
450   return SignalTimeout(nullptr); // nullptr means default context
451 }
452 
453 /**** Glib::SignalIdle *****************************************************/
454 
SignalIdle(GMainContext * context)455 inline SignalIdle::SignalIdle(GMainContext* context) : context_(context)
456 {
457 }
458 
459 sigc::connection
connect(const sigc::slot<bool> & slot,int priority)460 SignalIdle::connect(const sigc::slot<bool>& slot, int priority)
461 {
462   SourceConnectionNode* const conn_node = new SourceConnectionNode(slot);
463   const sigc::connection connection(*conn_node->get_slot());
464 
465   GSource* const source = g_idle_source_new();
466 
467   if (priority != G_PRIORITY_DEFAULT)
468     g_source_set_priority(source, priority);
469 
470   g_source_set_callback(
471     source, &glibmm_source_callback, conn_node, &SourceConnectionNode::destroy_notify_callback);
472 
473   conn_node->install(source);
474   g_source_attach(source, context_);
475   g_source_unref(source); // GMainContext holds a reference
476 
477   return connection;
478 }
479 
480 void
connect_once(const sigc::slot<void> & slot,int priority)481 SignalIdle::connect_once(const sigc::slot<void>& slot, int priority)
482 {
483   GSource* const source = g_idle_source_new();
484   glibmm_signal_connect_once(slot, priority, source, context_);
485 }
486 
487 SignalIdle
signal_idle()488 signal_idle()
489 {
490   return SignalIdle(nullptr); // nullptr means default context
491 }
492 
493 /**** Glib::SignalIO *******************************************************/
494 
SignalIO(GMainContext * context)495 inline SignalIO::SignalIO(GMainContext* context) : context_(context)
496 {
497 }
498 
499 sigc::connection
connect(const sigc::slot<bool,IOCondition> & slot,PollFD::fd_t fd,IOCondition condition,int priority)500 SignalIO::connect(
501   const sigc::slot<bool, IOCondition>& slot, PollFD::fd_t fd, IOCondition condition, int priority)
502 {
503   const auto source = IOSource::create(fd, condition);
504 
505   if (priority != G_PRIORITY_DEFAULT)
506     source->set_priority(priority);
507 
508   const sigc::connection connection = source->connect(slot);
509 
510   g_source_attach(source->gobj(), context_);
511 
512   return connection;
513 }
514 
515 sigc::connection
connect(const sigc::slot<bool,IOCondition> & slot,const Glib::RefPtr<IOChannel> & channel,IOCondition condition,int priority)516 SignalIO::connect(const sigc::slot<bool, IOCondition>& slot, const Glib::RefPtr<IOChannel>& channel,
517   IOCondition condition, int priority)
518 {
519   const auto source = IOSource::create(channel, condition);
520 
521   if (priority != G_PRIORITY_DEFAULT)
522     source->set_priority(priority);
523 
524   const sigc::connection connection = source->connect(slot);
525 
526   g_source_attach(source->gobj(), context_);
527 
528   return connection;
529 }
530 
531 SignalIO
signal_io()532 signal_io()
533 {
534   return SignalIO(nullptr); // nullptr means default context
535 }
536 
537 /**** Glib::SignalChildWatch **************************************************/
538 
SignalChildWatch(GMainContext * context)539 inline SignalChildWatch::SignalChildWatch(GMainContext* context) : context_(context)
540 {
541 }
542 
543 sigc::connection
connect(const sigc::slot<void,GPid,int> & slot,GPid pid,int priority)544 SignalChildWatch::connect(const sigc::slot<void, GPid, int>& slot, GPid pid, int priority)
545 {
546   SourceConnectionNode* const conn_node = new SourceConnectionNode(slot);
547   const sigc::connection connection(*conn_node->get_slot());
548 
549   GSource* const source = g_child_watch_source_new(pid);
550 
551   if (priority != G_PRIORITY_DEFAULT)
552     g_source_set_priority(source, priority);
553 
554   g_source_set_callback(source, Glib::function_pointer_cast<GSourceFunc>(&glibmm_child_watch_callback),
555     conn_node, &SourceConnectionNode::destroy_notify_callback);
556 
557   conn_node->install(source);
558   g_source_attach(source, context_);
559   g_source_unref(source); // GMainContext holds a reference
560 
561   return connection;
562 }
563 
564 SignalChildWatch
signal_child_watch()565 signal_child_watch()
566 {
567   return SignalChildWatch(nullptr); // nullptr means default context
568 }
569 
570 /**** Glib::MainContext ****************************************************/
571 
572 // static
573 Glib::RefPtr<MainContext>
create()574 MainContext::create()
575 {
576   return Glib::RefPtr<MainContext>(reinterpret_cast<MainContext*>(g_main_context_new()));
577 }
578 
579 // static
580 Glib::RefPtr<MainContext>
get_default()581 MainContext::get_default()
582 {
583   return Glib::wrap(g_main_context_default(), true);
584 }
585 
586 bool
iteration(bool may_block)587 MainContext::iteration(bool may_block)
588 {
589   return g_main_context_iteration(gobj(), may_block);
590 }
591 
592 bool
pending()593 MainContext::pending()
594 {
595   return g_main_context_pending(gobj());
596 }
597 
598 void
wakeup()599 MainContext::wakeup()
600 {
601   g_main_context_wakeup(gobj());
602 }
603 
604 bool
acquire()605 MainContext::acquire()
606 {
607   return g_main_context_acquire(gobj());
608 }
609 
610 #ifndef GLIBMM_DISABLE_DEPRECATED
611 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
612 bool
wait(Glib::Cond & cond,Glib::Mutex & mutex)613 MainContext::wait(Glib::Cond& cond, Glib::Mutex& mutex)
614 {
615   return g_main_context_wait(gobj(), cond.gobj(), mutex.gobj());
616 }
617 
618 bool
wait(Glib::Threads::Cond & cond,Glib::Threads::Mutex & mutex)619 MainContext::wait(Glib::Threads::Cond& cond, Glib::Threads::Mutex& mutex)
620 {
621   return g_main_context_wait(gobj(), cond.gobj(), mutex.gobj());
622 }
623 G_GNUC_END_IGNORE_DEPRECATIONS
624 #endif // GLIBMM_DISABLE_DEPRECATED
625 
626 void
release()627 MainContext::release()
628 {
629   g_main_context_release(gobj());
630 }
631 
632 bool
prepare(int & priority)633 MainContext::prepare(int& priority)
634 {
635   return g_main_context_prepare(gobj(), &priority);
636 }
637 
638 bool
prepare()639 MainContext::prepare()
640 {
641   return g_main_context_prepare(gobj(), nullptr);
642 }
643 
644 void
query(int max_priority,int & timeout,std::vector<PollFD> & fds)645 MainContext::query(int max_priority, int& timeout, std::vector<PollFD>& fds)
646 {
647   if (fds.empty())
648     fds.resize(8); // rather bogus number, but better than 0
649 
650   for (;;)
651   {
652     const int size_before = fds.size();
653     const int size_needed = g_main_context_query(
654       gobj(), max_priority, &timeout, reinterpret_cast<GPollFD*>(&fds.front()), size_before);
655 
656     fds.resize(size_needed);
657 
658     if (size_needed <= size_before)
659       break;
660   }
661 }
662 
663 bool
check(int max_priority,std::vector<PollFD> & fds)664 MainContext::check(int max_priority, std::vector<PollFD>& fds)
665 {
666   if (!fds.empty())
667     return g_main_context_check(
668       gobj(), max_priority, reinterpret_cast<GPollFD*>(&fds.front()), fds.size());
669   else
670     return false;
671 }
672 
673 void
dispatch()674 MainContext::dispatch()
675 {
676   g_main_context_dispatch(gobj());
677 }
678 
679 void
set_poll_func(GPollFunc poll_func)680 MainContext::set_poll_func(GPollFunc poll_func)
681 {
682   g_main_context_set_poll_func(gobj(), poll_func);
683 }
684 
685 GPollFunc
get_poll_func()686 MainContext::get_poll_func()
687 {
688   return g_main_context_get_poll_func(gobj());
689 }
690 
691 void
add_poll(PollFD & fd,int priority)692 MainContext::add_poll(PollFD& fd, int priority)
693 {
694   g_main_context_add_poll(gobj(), fd.gobj(), priority);
695 }
696 
697 void
remove_poll(PollFD & fd)698 MainContext::remove_poll(PollFD& fd)
699 {
700   g_main_context_remove_poll(gobj(), fd.gobj());
701 }
702 
703 void
push_thread_default()704 MainContext::push_thread_default()
705 {
706   g_main_context_push_thread_default(gobj());
707 }
708 
709 void
pop_thread_default()710 MainContext::pop_thread_default()
711 {
712   g_main_context_pop_thread_default(gobj());
713 }
714 
715 // static
716 Glib::RefPtr<MainContext>
get_thread_default()717 MainContext::get_thread_default()
718 {
719   // g_main_context_ref_thread_default() gives us a ref.
720   return Glib::wrap(g_main_context_ref_thread_default(), false);
721 }
722 
723 void
invoke(const sigc::slot<bool> & slot,int priority)724 MainContext::invoke(const sigc::slot<bool>& slot, int priority)
725 {
726   // Make a copy of slot on the heap.
727   sigc::slot_base* const slot_copy = new sigc::slot<bool>(slot);
728 
729   g_main_context_invoke_full(gobj(), priority, glibmm_main_context_invoke_callback, slot_copy,
730     glibmm_main_context_invoke_destroy_notify_callback);
731 }
732 
733 SignalTimeout
signal_timeout()734 MainContext::signal_timeout()
735 {
736   return SignalTimeout(gobj());
737 }
738 
739 SignalIdle
signal_idle()740 MainContext::signal_idle()
741 {
742   return SignalIdle(gobj());
743 }
744 
745 SignalIO
signal_io()746 MainContext::signal_io()
747 {
748   return SignalIO(gobj());
749 }
750 
751 SignalChildWatch
signal_child_watch()752 MainContext::signal_child_watch()
753 {
754   return SignalChildWatch(gobj());
755 }
756 
757 void
reference() const758 MainContext::reference() const
759 {
760   g_main_context_ref(reinterpret_cast<GMainContext*>(const_cast<MainContext*>(this)));
761 }
762 
763 void
unreference() const764 MainContext::unreference() const
765 {
766   g_main_context_unref(reinterpret_cast<GMainContext*>(const_cast<MainContext*>(this)));
767 }
768 
769 GMainContext*
gobj()770 MainContext::gobj()
771 {
772   return reinterpret_cast<GMainContext*>(this);
773 }
774 
775 const GMainContext*
gobj() const776 MainContext::gobj() const
777 {
778   return reinterpret_cast<const GMainContext*>(this);
779 }
780 
781 GMainContext*
gobj_copy() const782 MainContext::gobj_copy() const
783 {
784   reference();
785   return const_cast<GMainContext*>(gobj());
786 }
787 
788 Glib::RefPtr<MainContext>
wrap(GMainContext * gobject,bool take_copy)789 wrap(GMainContext* gobject, bool take_copy)
790 {
791   if (take_copy && gobject)
792     g_main_context_ref(gobject);
793 
794   return Glib::RefPtr<MainContext>(reinterpret_cast<MainContext*>(gobject));
795 }
796 
797 /**** Glib::MainLoop *******************************************************/
798 
799 Glib::RefPtr<MainLoop>
create(bool is_running)800 MainLoop::create(bool is_running)
801 {
802   return Glib::RefPtr<MainLoop>(reinterpret_cast<MainLoop*>(g_main_loop_new(nullptr, is_running)));
803 }
804 
805 Glib::RefPtr<MainLoop>
create(const Glib::RefPtr<MainContext> & context,bool is_running)806 MainLoop::create(const Glib::RefPtr<MainContext>& context, bool is_running)
807 {
808   return Glib::RefPtr<MainLoop>(
809     reinterpret_cast<MainLoop*>(g_main_loop_new(Glib::unwrap(context), is_running)));
810 }
811 
812 void
run()813 MainLoop::run()
814 {
815   g_main_loop_run(gobj());
816 }
817 
818 void
quit()819 MainLoop::quit()
820 {
821   g_main_loop_quit(gobj());
822 }
823 
824 bool
is_running()825 MainLoop::is_running()
826 {
827   return g_main_loop_is_running(gobj());
828 }
829 
830 Glib::RefPtr<MainContext>
get_context()831 MainLoop::get_context()
832 {
833   return Glib::wrap(g_main_loop_get_context(gobj()), true);
834 }
835 
836 // static:
837 int
depth()838 MainLoop::depth()
839 {
840   return g_main_depth();
841 }
842 
843 void
reference() const844 MainLoop::reference() const
845 {
846   g_main_loop_ref(reinterpret_cast<GMainLoop*>(const_cast<MainLoop*>(this)));
847 }
848 
849 void
unreference() const850 MainLoop::unreference() const
851 {
852   g_main_loop_unref(reinterpret_cast<GMainLoop*>(const_cast<MainLoop*>(this)));
853 }
854 
855 GMainLoop*
gobj()856 MainLoop::gobj()
857 {
858   return reinterpret_cast<GMainLoop*>(this);
859 }
860 
861 const GMainLoop*
gobj() const862 MainLoop::gobj() const
863 {
864   return reinterpret_cast<const GMainLoop*>(this);
865 }
866 
867 GMainLoop*
gobj_copy() const868 MainLoop::gobj_copy() const
869 {
870   reference();
871   return const_cast<GMainLoop*>(gobj());
872 }
873 
874 Glib::RefPtr<MainLoop>
wrap(GMainLoop * gobject,bool take_copy)875 wrap(GMainLoop* gobject, bool take_copy)
876 {
877   if (take_copy && gobject)
878     g_main_loop_ref(gobject);
879 
880   return Glib::RefPtr<MainLoop>(reinterpret_cast<MainLoop*>(gobject));
881 }
882 
883 /**** Glib::Source *********************************************************/
884 
885 // static
886 const GSourceFuncs Source::vfunc_table_ = {
887   &Source::prepare_vfunc, &Source::check_vfunc, &Source::dispatch_vfunc,
888   // We can't use finalize_vfunc because there is no way
889   // to store a pointer to our wrapper anywhere in GSource so
890   // that it persists until finalize_vfunc would be called from here.
891   nullptr, // finalize_vfunc
892   nullptr, // closure_callback
893   nullptr, // closure_marshal
894 };
895 
896 unsigned int
attach(const Glib::RefPtr<MainContext> & context)897 Source::attach(const Glib::RefPtr<MainContext>& context)
898 {
899   return g_source_attach(gobject_, Glib::unwrap(context));
900 }
901 
902 unsigned int
attach()903 Source::attach()
904 {
905   return g_source_attach(gobject_, nullptr);
906 }
907 
908 void
destroy()909 Source::destroy()
910 {
911   g_source_destroy(gobject_);
912 }
913 
914 void
set_priority(int priority)915 Source::set_priority(int priority)
916 {
917   g_source_set_priority(gobject_, priority);
918 }
919 
920 int
get_priority() const921 Source::get_priority() const
922 {
923   return g_source_get_priority(gobject_);
924 }
925 
926 void
set_can_recurse(bool can_recurse)927 Source::set_can_recurse(bool can_recurse)
928 {
929   g_source_set_can_recurse(gobject_, can_recurse);
930 }
931 
932 bool
get_can_recurse() const933 Source::get_can_recurse() const
934 {
935   return g_source_get_can_recurse(gobject_);
936 }
937 
938 unsigned int
get_id() const939 Source::get_id() const
940 {
941   return g_source_get_id(gobject_);
942 }
943 
944 Glib::RefPtr<MainContext>
get_context()945 Source::get_context()
946 {
947   return Glib::wrap(g_source_get_context(gobject_), true);
948 }
949 
950 GSource*
gobj_copy() const951 Source::gobj_copy() const
952 {
953   return g_source_ref(gobject_);
954 }
955 
956 void
reference() const957 Source::reference() const
958 {
959   std::lock_guard<std::mutex> lock(extra_source_data_mutex);
960   ++extra_source_data[this].ref_count;
961 }
962 
963 void
unreference() const964 Source::unreference() const
965 {
966   std::unique_lock<std::mutex> lock(extra_source_data_mutex);
967   if (--extra_source_data[this].ref_count == 0)
968   {
969     GSource* const tmp_gobject = gobject_;
970 
971     if (--extra_source_data[this].keep_wrapper == 0)
972     {
973       // The last reference from a RefPtr<Source> has been deleted, and
974       // SourceCallbackData::destroy_notify_callback() has been called while
975       // extra_source_data[this].keep_wrapper was > 1.
976       // Delete the wrapper!
977       extra_source_data.erase(this);
978       lock.unlock();
979       destroy_notify_callback(const_cast<Source*>(this));
980     }
981     else
982       lock.unlock();
983 
984     // Drop the one and only GSource reference held by the C++ wrapper.
985     // If the GSource instance is attached to a main context, the GMainContext
986     // holds a reference until the source is detached (destroyed).
987     g_source_unref(tmp_gobject);
988   }
989 }
990 
Source()991 Source::Source() : gobject_(g_source_new(const_cast<GSourceFuncs*>(&vfunc_table_), sizeof(GSource)))
992 {
993   g_source_set_callback(gobject_, &glibmm_dummy_source_callback,
994     new SourceCallbackData(this), // our persistent callback data object
995     &SourceCallbackData::destroy_notify_callback);
996 }
997 
Source(GSource * cast_item,GSourceFunc callback_func)998 Source::Source(GSource* cast_item, GSourceFunc callback_func) : gobject_(cast_item)
999 {
1000   g_source_set_callback(gobject_, callback_func,
1001     new SourceCallbackData(this), // our persistent callback data object
1002     &SourceCallbackData::destroy_notify_callback);
1003 }
1004 
~Source()1005 Source::~Source() noexcept
1006 {
1007   // The dtor should be invoked by destroy_notify_callback() only, which clears
1008   // gobject_ before deleting.  However, we might also get to this point if
1009   // a derived ctor threw an exception, and then we need to unref manually.
1010 
1011   if (gobject_)
1012   {
1013     SourceCallbackData* const data = glibmm_source_get_callback_data(gobject_);
1014 
1015     if (data) {
1016       data->wrapper = nullptr;
1017     }
1018 
1019     GSource* const tmp_gobject = gobject_;
1020     gobject_ = nullptr;
1021 
1022     g_source_unref(tmp_gobject);
1023 
1024     // The constructor does not add this to extra_source_data. No need to erase.
1025   }
1026 }
1027 
1028 sigc::connection
connect_generic(const sigc::slot_base & slot)1029 Source::connect_generic(const sigc::slot_base& slot)
1030 {
1031   SourceConnectionNode* const conn_node = new SourceConnectionNode(slot);
1032   const sigc::connection connection(*conn_node->get_slot());
1033 
1034   // Don't override the callback data.  Reuse the existing one
1035   // calling SourceCallbackData::set_node() to register conn_node.
1036   SourceCallbackData* const data = glibmm_source_get_callback_data(gobject_);
1037 
1038   if (data) {
1039     data->set_node(conn_node);
1040   }
1041 
1042   conn_node->install(gobject_);
1043   return connection;
1044 }
1045 
1046 void
add_poll(Glib::PollFD & poll_fd)1047 Source::add_poll(Glib::PollFD& poll_fd)
1048 {
1049   g_source_add_poll(gobject_, poll_fd.gobj());
1050 }
1051 
1052 void
remove_poll(Glib::PollFD & poll_fd)1053 Source::remove_poll(Glib::PollFD& poll_fd)
1054 {
1055   g_source_remove_poll(gobject_, poll_fd.gobj());
1056 }
1057 
1058 #ifndef GLIBMM_DISABLE_DEPRECATED
1059 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1060 void
get_current_time(Glib::TimeVal & current_time)1061 Source::get_current_time(Glib::TimeVal& current_time)
1062 {
1063   g_source_get_current_time(gobject_, &current_time);
1064 }
1065 G_GNUC_END_IGNORE_DEPRECATIONS
1066 #endif // GLIBMM_DISABLE_DEPRECATED
1067 
1068 gint64
get_time() const1069 Source::get_time() const
1070 {
1071   if (g_source_get_context(const_cast<GSource*>(gobject_)))
1072     return g_source_get_time(const_cast<GSource*>(gobject_));
1073   else
1074     return g_get_monotonic_time();
1075 }
1076 
1077 inline // static
1078   Source*
get_wrapper(GSource * source)1079   Source::get_wrapper(GSource* source)
1080 {
1081   SourceCallbackData* const data = glibmm_source_get_callback_data(source);
1082   return (data) ? data->wrapper : nullptr;
1083 }
1084 
1085 // static
1086 gboolean
prepare_vfunc(GSource * source,int * timeout)1087 Source::prepare_vfunc(GSource* source, int* timeout)
1088 {
1089   try
1090   {
1091     Source* const self = get_wrapper(source);
1092     return (self) ? self->prepare(*timeout) : 0;
1093   }
1094   catch (...)
1095   {
1096     Glib::exception_handlers_invoke();
1097   }
1098 
1099   return 0;
1100 }
1101 
1102 // static
1103 gboolean
check_vfunc(GSource * source)1104 Source::check_vfunc(GSource* source)
1105 {
1106   try
1107   {
1108     Source* const self = get_wrapper(source);
1109     return (self) ? self->check() : 0;
1110   }
1111   catch (...)
1112   {
1113     Glib::exception_handlers_invoke();
1114   }
1115 
1116   return 0;
1117 }
1118 
1119 // static
1120 gboolean
dispatch_vfunc(GSource *,GSourceFunc callback,void * user_data)1121 Source::dispatch_vfunc(GSource*, GSourceFunc callback, void* user_data)
1122 {
1123   SourceCallbackData* const callback_data = static_cast<SourceCallbackData*>(user_data);
1124 
1125   g_return_val_if_fail(callback == &glibmm_dummy_source_callback, 0);
1126   g_return_val_if_fail(callback_data != nullptr && callback_data->node != nullptr, 0);
1127 
1128   try
1129   {
1130     Source* const self = callback_data->wrapper;
1131     return self->dispatch(callback_data->node->get_slot());
1132   }
1133   catch (...)
1134   {
1135     Glib::exception_handlers_invoke();
1136   }
1137   return 0;
1138 }
1139 
1140 // static
1141 void
destroy_notify_callback(void * data)1142 Source::destroy_notify_callback(void* data)
1143 {
1144   if (data)
1145   {
1146     Source* const self = static_cast<Source*>(data);
1147 
1148     // gobject_ is already invalid at this point.
1149     self->gobject_ = nullptr;
1150 
1151     // No exception checking: if the dtor throws, you're out of luck anyway.
1152     delete self;
1153   }
1154 }
1155 
1156 // static
1157 sigc::connection
attach_signal_source(const sigc::slot_base & slot,int priority,GSource * source,GMainContext * context,GSourceFunc callback_func)1158 Source::attach_signal_source(const sigc::slot_base& slot, int priority, GSource* source,
1159   GMainContext* context, GSourceFunc callback_func)
1160 {
1161   SourceConnectionNode* const conn_node = new SourceConnectionNode(slot);
1162   const sigc::connection connection(*conn_node->get_slot());
1163 
1164   if (priority != G_PRIORITY_DEFAULT)
1165     g_source_set_priority(source, priority);
1166 
1167   g_source_set_callback(
1168     source, callback_func, conn_node, &SourceConnectionNode::destroy_notify_callback);
1169 
1170   conn_node->install(source);
1171   g_source_attach(source, context);
1172   g_source_unref(source); // GMainContext holds a reference
1173 
1174   return connection;
1175 }
1176 
1177 // static
1178 sigc::slot_base*
get_slot_from_connection_node(void * data)1179 Source::get_slot_from_connection_node(void* data)
1180 {
1181   return static_cast<SourceConnectionNode*>(data)->get_slot();
1182 }
1183 
1184 // static
1185 sigc::slot_base*
get_slot_from_callback_data(void * data)1186 Source::get_slot_from_callback_data(void* data)
1187 {
1188   SourceCallbackData* const callback_data = static_cast<SourceCallbackData*>(data);
1189   g_return_val_if_fail(callback_data->node != nullptr, nullptr);
1190   return callback_data->node->get_slot();
1191 }
1192 
1193 /**** Glib::TimeoutSource **************************************************/
1194 
1195 // static
1196 Glib::RefPtr<TimeoutSource>
create(unsigned int interval)1197 TimeoutSource::create(unsigned int interval)
1198 {
1199   return Glib::RefPtr<TimeoutSource>(new TimeoutSource(interval));
1200 }
1201 
1202 sigc::connection
connect(const sigc::slot<bool> & slot)1203 TimeoutSource::connect(const sigc::slot<bool>& slot)
1204 {
1205   return connect_generic(slot);
1206 }
1207 
TimeoutSource(unsigned int interval)1208 TimeoutSource::TimeoutSource(unsigned int interval) : interval_(interval)
1209 {
1210   time64_to_time_val(get_time(), expiration_);
1211   expiration_.add_milliseconds(std::min<unsigned long>(G_MAXLONG, interval_));
1212 }
1213 
~TimeoutSource()1214 TimeoutSource::~TimeoutSource() noexcept
1215 {
1216 }
1217 
1218 bool
prepare(int & timeout)1219 TimeoutSource::prepare(int& timeout)
1220 {
1221   Glib::TimeVal current_time;
1222   time64_to_time_val(get_time(), current_time);
1223 
1224   Glib::TimeVal remaining = expiration_;
1225   remaining.subtract(current_time);
1226 
1227   if (remaining.negative())
1228   {
1229     // Already expired.
1230     timeout = 0;
1231   }
1232   else
1233   {
1234     const unsigned long milliseconds = static_cast<unsigned long>(remaining.tv_sec) * 1000U +
1235                                        static_cast<unsigned long>(remaining.tv_usec) / 1000U;
1236 
1237     // Set remaining milliseconds.
1238     timeout = std::min<unsigned long>(G_MAXINT, milliseconds);
1239 
1240     // Check if the system time has been set backwards. (remaining > interval)
1241     remaining.add_milliseconds(-std::min<unsigned long>(G_MAXLONG, interval_) - 1);
1242     if (!remaining.negative())
1243     {
1244       // Oh well.  Reset the expiration time to now + interval;
1245       // this at least avoids hanging for long periods of time.
1246       expiration_ = current_time;
1247       expiration_.add_milliseconds(interval_);
1248       timeout = std::min<unsigned int>(G_MAXINT, interval_);
1249     }
1250   }
1251 
1252   return (timeout == 0);
1253 }
1254 
1255 bool
check()1256 TimeoutSource::check()
1257 {
1258   Glib::TimeVal current_time;
1259   time64_to_time_val(get_time(), current_time);
1260 
1261   return (expiration_ <= current_time);
1262 }
1263 
1264 bool
dispatch(sigc::slot_base * slot)1265 TimeoutSource::dispatch(sigc::slot_base* slot)
1266 {
1267   const bool again = (*static_cast<sigc::slot<bool>*>(slot))();
1268 
1269   if (again)
1270   {
1271     time64_to_time_val(get_time(), expiration_);
1272     expiration_.add_milliseconds(std::min<unsigned long>(G_MAXLONG, interval_));
1273   }
1274 
1275   return again;
1276 }
1277 
1278 /**** Glib::IdleSource *****************************************************/
1279 
1280 // static
1281 Glib::RefPtr<IdleSource>
create()1282 IdleSource::create()
1283 {
1284   return Glib::RefPtr<IdleSource>(new IdleSource());
1285 }
1286 
1287 sigc::connection
connect(const sigc::slot<bool> & slot)1288 IdleSource::connect(const sigc::slot<bool>& slot)
1289 {
1290   return connect_generic(slot);
1291 }
1292 
IdleSource()1293 IdleSource::IdleSource()
1294 {
1295   set_priority(PRIORITY_DEFAULT_IDLE);
1296 }
1297 
~IdleSource()1298 IdleSource::~IdleSource() noexcept
1299 {
1300 }
1301 
1302 bool
prepare(int & timeout)1303 IdleSource::prepare(int& timeout)
1304 {
1305   timeout = 0;
1306   return true;
1307 }
1308 
1309 bool
check()1310 IdleSource::check()
1311 {
1312   return true;
1313 }
1314 
1315 bool
dispatch(sigc::slot_base * slot)1316 IdleSource::dispatch(sigc::slot_base* slot)
1317 {
1318   return (*static_cast<sigc::slot<bool>*>(slot))();
1319 }
1320 
1321 /**** Glib::IOSource *******************************************************/
1322 
1323 // static
1324 Glib::RefPtr<IOSource>
create(PollFD::fd_t fd,IOCondition condition)1325 IOSource::create(PollFD::fd_t fd, IOCondition condition)
1326 {
1327   return Glib::RefPtr<IOSource>(new IOSource(fd, condition));
1328 }
1329 
1330 Glib::RefPtr<IOSource>
create(const Glib::RefPtr<IOChannel> & channel,IOCondition condition)1331 IOSource::create(const Glib::RefPtr<IOChannel>& channel, IOCondition condition)
1332 {
1333   return Glib::RefPtr<IOSource>(new IOSource(channel, condition));
1334 }
1335 
1336 sigc::connection
connect(const sigc::slot<bool,IOCondition> & slot)1337 IOSource::connect(const sigc::slot<bool, IOCondition>& slot)
1338 {
1339   return connect_generic(slot);
1340 }
1341 
IOSource(PollFD::fd_t fd,IOCondition condition)1342 IOSource::IOSource(PollFD::fd_t fd, IOCondition condition) : poll_fd_(fd, condition)
1343 {
1344   add_poll(poll_fd_);
1345 }
1346 
IOSource(const Glib::RefPtr<IOChannel> & channel,IOCondition condition)1347 IOSource::IOSource(const Glib::RefPtr<IOChannel>& channel, IOCondition condition)
1348 : Source(g_io_create_watch(channel->gobj(), (GIOCondition)condition),
1349     Glib::function_pointer_cast<GSourceFunc>(&glibmm_iosource_callback))
1350 {
1351 }
1352 
IOSource(GSource * cast_item,GSourceFunc callback_func)1353 IOSource::IOSource(GSource* cast_item, GSourceFunc callback_func) : Source(cast_item, callback_func)
1354 {
1355 }
1356 
~IOSource()1357 IOSource::~IOSource() noexcept
1358 {
1359 }
1360 
1361 bool
prepare(int & timeout)1362 IOSource::prepare(int& timeout)
1363 {
1364   timeout = -1;
1365   return false;
1366 }
1367 
1368 bool
check()1369 IOSource::check()
1370 {
1371   return ((poll_fd_.get_revents() & poll_fd_.get_events()) != 0);
1372 }
1373 
1374 bool
dispatch(sigc::slot_base * slot)1375 IOSource::dispatch(sigc::slot_base* slot)
1376 {
1377   return (*static_cast<sigc::slot<bool, IOCondition>*>(slot))(poll_fd_.get_revents());
1378 }
1379 
1380 } // namespace Glib
1381