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