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