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; 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 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 }; 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 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 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; 88 timeout_handler(DBusTimeout *t) : dbus_timeout(t) {} 89 void operator()(boost::system::error_code ec) { 90 if (ec) return; 91 dbus_timeout_handle(dbus_timeout); 92 } 93 }; 94 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 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 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; 131 dispatch_handler(boost::asio::io_service &i, DBusConnection *c) 132 : io(i), conn(c) {} 133 void operator()() { 134 if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) 135 io.post(dispatch_handler(io, conn)); 136 } 137 }; 138 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 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