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 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 }; 33 static 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 51 static 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 72 static 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 77 struct 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 86 static 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 101 static 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 114 static void remove_timeout(DBusTimeout *dbus_timeout, void *data) { 115 delete static_cast<boost::asio::steady_timer *>(dbus_timeout_get_data(dbus_timeout)); 116 } 117 118 struct 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 129 static 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 136 static 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 152