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 
70   if(fd == -1)
71     // socket based watches
72     fd = dbus_watch_get_socket(dbus_watch);
73 
74   stream_protocol::socket& socket =
75     *new stream_protocol::socket(io);
76 
77   socket.assign(stream_protocol(0,0), fd);
78 
79   dbus_watch_set_data(dbus_watch, &socket, NULL);
80 
81   watch_toggled(dbus_watch, &io);
82   return TRUE;
83 }
84 
85 static void remove_watch(DBusWatch *dbus_watch, void *data)
86 {
87   delete static_cast<stream_protocol::socket *>(dbus_watch_get_data(dbus_watch));
88 }
89 
90 
91 
92 
93 struct timeout_handler
94 {
95   DBusTimeout* dbus_timeout;
96   timeout_handler(DBusTimeout* t):
97     dbus_timeout(t) {}
98 
99   void operator()(boost::system::error_code ec)
100   {
101     if(ec) return;
102     dbus_timeout_handle(dbus_timeout);
103   }
104 };
105 
106 static void timeout_toggled(DBusTimeout *dbus_timeout, void *data)
107 {
108   steady_timer& timer =
109     *static_cast<steady_timer *>(dbus_timeout_get_data(dbus_timeout));
110 
111   if(dbus_timeout_get_enabled(dbus_timeout)) {
112     steady_timer::duration interval = chrono::milliseconds(dbus_timeout_get_interval(dbus_timeout));
113     timer.expires_from_now(interval);
114     timer.cancel();
115     timer.async_wait(timeout_handler(dbus_timeout));
116   } else {
117     timer.cancel();
118   }
119 }
120 
121 static dbus_bool_t add_timeout(DBusTimeout *dbus_timeout, void *data)
122 {
123   if(!dbus_timeout_get_enabled(dbus_timeout)) return TRUE;
124 
125   io_service& io = *static_cast<io_service *>(data);
126 
127   steady_timer& timer =
128     *new steady_timer(io);
129 
130   dbus_timeout_set_data(dbus_timeout, &timer, NULL);
131 
132   timeout_toggled(dbus_timeout, &io);
133   return TRUE;
134 }
135 
136 static void remove_timeout(DBusTimeout *dbus_timeout, void *data)
137 {
138   delete static_cast<steady_timer *>(dbus_timeout_get_data(dbus_timeout));
139 }
140 
141 
142 struct dispatch_handler
143 {
144   boost::asio::io_service& io;
145   DBusConnection *conn;
146   dispatch_handler(boost::asio::io_service& i, DBusConnection *c): io(i), conn(c) {}
147   void operator()() {
148     if(dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS)
149       io.post(dispatch_handler(io, conn));
150   }
151 };
152 
153 static void dispatch_status(DBusConnection *conn, DBusDispatchStatus new_status, void *data)
154 {
155   io_service& io = *static_cast<io_service *>(data);
156   if(new_status == DBUS_DISPATCH_DATA_REMAINS)
157     io.post(dispatch_handler(io, conn));
158 }
159 
160 static void set_watch_timeout_dispatch_functions(DBusConnection *conn, boost::asio::io_service& io)
161 {
162     dbus_connection_set_watch_functions(conn,
163       &add_watch, &remove_watch, &watch_toggled, &io, NULL);
164 
165     dbus_connection_set_timeout_functions(conn,
166       &add_timeout, &remove_timeout, &timeout_toggled, &io, NULL);
167 
168     dbus_connection_set_dispatch_status_function(conn,
169       &dispatch_status, &io, NULL);
170 }
171 
172 } // namespace detail
173 } // namespace dbus
174 
175 #endif // DBUS_WATCH_TIMEOUT_HPP
176