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 <dbus/chrono.hpp>
14 
15 namespace dbus {
16 namespace detail {
17 
18 using boost::asio::io_service;
19 using boost::asio::steady_timer;
20 using boost::asio::generic::stream_protocol;
21 using boost::asio::null_buffers;
22 
23 static void watch_toggled(DBusWatch *dbus_watch, void *data);
24 struct watch_handler
25 {
26   DBusWatchFlags flags;
27   DBusWatch* dbus_watch;
28   watch_handler(DBusWatchFlags f, DBusWatch* w):
29     flags(f), dbus_watch(w) {}
30 
31   void operator()(boost::system::error_code ec, size_t)
32   {
33     if(ec) return;
34     dbus_watch_handle(dbus_watch, flags);
35 
36     stream_protocol::socket& socket =
37       *static_cast<stream_protocol::socket *>(dbus_watch_get_data(dbus_watch));
38 
39     watch_toggled(dbus_watch, &socket.get_io_service());
40   }
41 };
42 static void watch_toggled(DBusWatch *dbus_watch, void *data)
43 {
44   stream_protocol::socket& socket =
45     *static_cast<stream_protocol::socket *>(dbus_watch_get_data(dbus_watch));
46 
47   if(dbus_watch_get_enabled(dbus_watch)) {
48 
49     if(dbus_watch_get_flags(dbus_watch) & DBUS_WATCH_READABLE)
50       socket.async_read_some(null_buffers(),
51         watch_handler(DBUS_WATCH_READABLE, dbus_watch));
52 
53     if(dbus_watch_get_flags(dbus_watch) & DBUS_WATCH_WRITABLE)
54       socket.async_write_some(null_buffers(),
55         watch_handler(DBUS_WATCH_WRITABLE, dbus_watch));
56 
57   } else {
58     socket.cancel();
59   }
60 }
61 
62 static dbus_bool_t add_watch(DBusWatch *dbus_watch, void *data)
63 {
64   if(!dbus_watch_get_enabled(dbus_watch)) return TRUE;
65 
66   io_service& io = *static_cast<io_service *>(data);
67 
68   int fd = dbus_watch_get_unix_fd(dbus_watch);
69   stream_protocol::socket& socket =
70     *new stream_protocol::socket(io);
71 
72   socket.assign(stream_protocol(0,0), fd);
73 
74   dbus_watch_set_data(dbus_watch, &socket, NULL);
75 
76   watch_toggled(dbus_watch, &io);
77   return TRUE;
78 }
79 
80 static void remove_watch(DBusWatch *dbus_watch, void *data)
81 {
82   delete static_cast<stream_protocol::socket *>(dbus_watch_get_data(dbus_watch));
83 }
84 
85 
86 
87 
88 struct timeout_handler
89 {
90   DBusTimeout* dbus_timeout;
91   timeout_handler(DBusTimeout* t):
92     dbus_timeout(t) {}
93 
94   void operator()(boost::system::error_code ec)
95   {
96     if(ec) return;
97     dbus_timeout_handle(dbus_timeout);
98   }
99 };
100 
101 static void timeout_toggled(DBusTimeout *dbus_timeout, void *data)
102 {
103   steady_timer& timer =
104     *static_cast<steady_timer *>(dbus_timeout_get_data(dbus_timeout));
105 
106   if(dbus_timeout_get_enabled(dbus_timeout)) {
107     steady_timer::duration interval = chrono::milliseconds(dbus_timeout_get_interval(dbus_timeout));
108     timer.expires_from_now(interval);
109     timer.cancel();
110     timer.async_wait(timeout_handler(dbus_timeout));
111   } else {
112     timer.cancel();
113   }
114 }
115 
116 static dbus_bool_t add_timeout(DBusTimeout *dbus_timeout, void *data)
117 {
118   if(!dbus_timeout_get_enabled(dbus_timeout)) return TRUE;
119 
120   io_service& io = *static_cast<io_service *>(data);
121 
122   steady_timer& timer =
123     *new steady_timer(io);
124 
125   dbus_timeout_set_data(dbus_timeout, &timer, NULL);
126 
127   timeout_toggled(dbus_timeout, &io);
128   return TRUE;
129 }
130 
131 static void remove_timeout(DBusTimeout *dbus_timeout, void *data)
132 {
133   delete static_cast<steady_timer *>(dbus_timeout_get_data(dbus_timeout));
134 }
135 
136 
137 struct dispatch_handler
138 {
139   boost::asio::io_service& io;
140   DBusConnection *conn;
141   dispatch_handler(boost::asio::io_service& i, DBusConnection *c): io(i), conn(c) {}
142   void operator()() {
143     if(dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS)
144       io.post(dispatch_handler(io, conn));
145   }
146 };
147 
148 static void dispatch_status(DBusConnection *conn, DBusDispatchStatus new_status, void *data)
149 {
150   io_service& io = *static_cast<io_service *>(data);
151   if(new_status == DBUS_DISPATCH_DATA_REMAINS)
152     io.post(dispatch_handler(io, conn));
153 }
154 
155 static void set_watch_timeout_dispatch_functions(DBusConnection *conn, boost::asio::io_service& io)
156 {
157     dbus_connection_set_watch_functions(conn,
158       &add_watch, &remove_watch, &watch_toggled, &io, NULL);
159 
160     dbus_connection_set_timeout_functions(conn,
161       &add_timeout, &remove_timeout, &timeout_toggled, &io, NULL);
162 
163     dbus_connection_set_dispatch_status_function(conn,
164       &dispatch_status, &io, NULL);
165 
166     dispatch_status(conn,
167       dbus_connection_get_dispatch_status(conn), &io);
168 }
169 
170 } // namespace detail
171 } // namespace dbus
172 
173 #endif // DBUS_WATCH_TIMEOUT_HPP
174