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