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_, ¤t_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