1 // Copyright (c) Benjamin Kietzman (github.com/bkietz)
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5
6 #ifndef DBUS_WATCH_TIMEOUT_HPP
7 #define DBUS_WATCH_TIMEOUT_HPP
8
9 #include <dbus/dbus.h>
10 #include <boost/asio/generic/stream_protocol.hpp>
11 #include <boost/asio/steady_timer.hpp>
12
13 #include <chrono>
14
15 namespace dbus {
16 namespace detail {
17
18 static void watch_toggled(DBusWatch *dbus_watch, void *data);
19 struct watch_handler {
20 DBusWatchFlags flags;
21 DBusWatch *dbus_watch;
watch_handlerdbus::detail::watch_handler22 watch_handler(DBusWatchFlags f, DBusWatch *w) : flags(f), dbus_watch(w) {}
operator ()dbus::detail::watch_handler23 void operator()(boost::system::error_code ec, size_t) {
24 if (ec) return;
25 dbus_watch_handle(dbus_watch, flags);
26 auto data = dbus_watch_get_data(dbus_watch);
27 if (data != nullptr) {
28 boost::asio::generic::stream_protocol::socket &socket =
29 *static_cast<boost::asio::generic::stream_protocol::socket *>(data);
30
31 watch_toggled(dbus_watch, &socket.get_io_service());
32 }
33 }
34 };
watch_toggled(DBusWatch * dbus_watch,void * data)35 static void watch_toggled(DBusWatch *dbus_watch, void *data) {
36 void *watch_data = dbus_watch_get_data(dbus_watch);
37 if (watch_data == nullptr) {
38 return;
39 }
40
41 auto socket =
42 static_cast<boost::asio::generic::stream_protocol::socket *>(watch_data);
43 if (dbus_watch_get_enabled(dbus_watch)) {
44 if (dbus_watch_get_flags(dbus_watch) & DBUS_WATCH_READABLE)
45 socket->async_read_some(boost::asio::null_buffers(),
46 watch_handler(DBUS_WATCH_READABLE, dbus_watch));
47
48 if (dbus_watch_get_flags(dbus_watch) & DBUS_WATCH_WRITABLE)
49 socket->async_write_some(boost::asio::null_buffers(),
50 watch_handler(DBUS_WATCH_WRITABLE, dbus_watch));
51
52 } else {
53 socket->cancel();
54 }
55 }
56
add_watch(DBusWatch * dbus_watch,void * data)57 static dbus_bool_t add_watch(DBusWatch *dbus_watch, void *data) {
58 if (!dbus_watch_get_enabled(dbus_watch)) {
59 return TRUE;
60 }
61
62 boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
63
64 int fd = dbus_watch_get_unix_fd(dbus_watch);
65
66 if (fd == -1)
67 // socket based watches
68 fd = dbus_watch_get_socket(dbus_watch);
69
70 boost::asio::generic::stream_protocol::socket &socket =
71 *new boost::asio::generic::stream_protocol::socket(io);
72
73 socket.assign(boost::asio::generic::stream_protocol(0, 0), fd);
74
75 dbus_watch_set_data(dbus_watch, &socket, NULL);
76
77 watch_toggled(dbus_watch, &io);
78 return TRUE;
79 }
80
remove_watch(DBusWatch * dbus_watch,void * data)81 static void remove_watch(DBusWatch *dbus_watch, void *data) {
82 delete static_cast<boost::asio::generic::stream_protocol::socket *>(
83 dbus_watch_get_data(dbus_watch));
84 }
85
86 struct timeout_handler {
87 DBusTimeout *dbus_timeout;
timeout_handlerdbus::detail::timeout_handler88 timeout_handler(DBusTimeout *t) : dbus_timeout(t) {}
operator ()dbus::detail::timeout_handler89 void operator()(boost::system::error_code ec) {
90 if (ec) return;
91 dbus_timeout_handle(dbus_timeout);
92 }
93 };
94
timeout_toggled(DBusTimeout * dbus_timeout,void * data)95 static void timeout_toggled(DBusTimeout *dbus_timeout, void *data) {
96 boost::asio::steady_timer &timer = *static_cast<boost::asio::steady_timer *>(
97 dbus_timeout_get_data(dbus_timeout));
98
99 if (dbus_timeout_get_enabled(dbus_timeout)) {
100 boost::asio::steady_timer::duration interval =
101 std::chrono::milliseconds(dbus_timeout_get_interval(dbus_timeout));
102 timer.expires_from_now(interval);
103 timer.cancel();
104 timer.async_wait(timeout_handler(dbus_timeout));
105 } else {
106 timer.cancel();
107 }
108 }
109
add_timeout(DBusTimeout * dbus_timeout,void * data)110 static dbus_bool_t add_timeout(DBusTimeout *dbus_timeout, void *data) {
111 if (!dbus_timeout_get_enabled(dbus_timeout)) return TRUE;
112
113 boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
114
115 boost::asio::steady_timer &timer = *new boost::asio::steady_timer(io);
116
117 dbus_timeout_set_data(dbus_timeout, &timer, NULL);
118
119 timeout_toggled(dbus_timeout, &io);
120 return TRUE;
121 }
122
remove_timeout(DBusTimeout * dbus_timeout,void * data)123 static void remove_timeout(DBusTimeout *dbus_timeout, void *data) {
124 delete static_cast<boost::asio::steady_timer *>(
125 dbus_timeout_get_data(dbus_timeout));
126 }
127
128 struct dispatch_handler {
129 boost::asio::io_service &io;
130 DBusConnection *conn;
dispatch_handlerdbus::detail::dispatch_handler131 dispatch_handler(boost::asio::io_service &i, DBusConnection *c)
132 : io(i), conn(c) {}
operator ()dbus::detail::dispatch_handler133 void operator()() {
134 if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS)
135 io.post(dispatch_handler(io, conn));
136 }
137 };
138
dispatch_status(DBusConnection * conn,DBusDispatchStatus new_status,void * data)139 static void dispatch_status(DBusConnection *conn, DBusDispatchStatus new_status,
140 void *data) {
141 boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
142 if (new_status == DBUS_DISPATCH_DATA_REMAINS)
143 io.post(dispatch_handler(io, conn));
144 }
145
set_watch_timeout_dispatch_functions(DBusConnection * conn,boost::asio::io_service & io)146 static void set_watch_timeout_dispatch_functions(DBusConnection *conn,
147 boost::asio::io_service &io) {
148 dbus_connection_set_watch_functions(conn, &add_watch, &remove_watch,
149 &watch_toggled, &io, NULL);
150
151 dbus_connection_set_timeout_functions(conn, &add_timeout, &remove_timeout,
152 &timeout_toggled, &io, NULL);
153
154 dbus_connection_set_dispatch_status_function(conn, &dispatch_status, &io,
155 NULL);
156 }
157
158 } // namespace detail
159 } // namespace dbus
160
161 #endif // DBUS_WATCH_TIMEOUT_HPP
162