blob: ef2e708a107377baf67cc5b80658fa69996ae9b9 [file] [log] [blame]
// Copyright (c) Benjamin Kietzman (github.com/bkietz)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef DBUS_WATCH_TIMEOUT_HPP
#define DBUS_WATCH_TIMEOUT_HPP
#include <dbus/dbus.h>
#include <boost/asio/generic/stream_protocol.hpp>
#include <boost/asio/steady_timer.hpp>
#include <chrono>
namespace dbus {
namespace detail {
static void watch_toggled(DBusWatch *dbus_watch, void *data);
struct watch_handler {
DBusWatchFlags flags;
DBusWatch *dbus_watch;
watch_handler(DBusWatchFlags f, DBusWatch *w) : flags(f), dbus_watch(w) {}
void operator()(boost::system::error_code ec, size_t) {
if (ec) return;
dbus_watch_handle(dbus_watch, flags);
boost::asio::generic::stream_protocol::socket &socket = *static_cast<boost::asio::generic::stream_protocol::socket *>(
dbus_watch_get_data(dbus_watch));
watch_toggled(dbus_watch, &socket.get_io_service());
}
};
static void watch_toggled(DBusWatch *dbus_watch, void *data) {
boost::asio::generic::stream_protocol::socket &socket =
*static_cast<boost::asio::generic::stream_protocol::socket *>(dbus_watch_get_data(dbus_watch));
if (dbus_watch_get_enabled(dbus_watch)) {
if (dbus_watch_get_flags(dbus_watch) & DBUS_WATCH_READABLE)
socket.async_read_some(boost::asio::null_buffers(),
watch_handler(DBUS_WATCH_READABLE, dbus_watch));
if (dbus_watch_get_flags(dbus_watch) & DBUS_WATCH_WRITABLE)
socket.async_write_some(boost::asio::null_buffers(),
watch_handler(DBUS_WATCH_WRITABLE, dbus_watch));
} else {
socket.cancel();
}
}
static dbus_bool_t add_watch(DBusWatch *dbus_watch, void *data) {
if (!dbus_watch_get_enabled(dbus_watch)) return TRUE;
boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
int fd = dbus_watch_get_unix_fd(dbus_watch);
if (fd == -1)
// socket based watches
fd = dbus_watch_get_socket(dbus_watch);
boost::asio::generic::stream_protocol::socket &socket = *new boost::asio::generic::stream_protocol::socket(io);
socket.assign(boost::asio::generic::stream_protocol(0, 0), fd);
dbus_watch_set_data(dbus_watch, &socket, NULL);
watch_toggled(dbus_watch, &io);
return TRUE;
}
static void remove_watch(DBusWatch *dbus_watch, void *data) {
delete static_cast<boost::asio::generic::stream_protocol::socket *>(
dbus_watch_get_data(dbus_watch));
}
struct timeout_handler {
DBusTimeout *dbus_timeout;
timeout_handler(DBusTimeout *t) : dbus_timeout(t) {}
void operator()(boost::system::error_code ec) {
if (ec) return;
dbus_timeout_handle(dbus_timeout);
}
};
static void timeout_toggled(DBusTimeout *dbus_timeout, void *data) {
boost::asio::steady_timer &timer =
*static_cast<boost::asio::steady_timer *>(dbus_timeout_get_data(dbus_timeout));
if (dbus_timeout_get_enabled(dbus_timeout)) {
boost::asio::steady_timer::duration interval =
std::chrono::milliseconds(dbus_timeout_get_interval(dbus_timeout));
timer.expires_from_now(interval);
timer.cancel();
timer.async_wait(timeout_handler(dbus_timeout));
} else {
timer.cancel();
}
}
static dbus_bool_t add_timeout(DBusTimeout *dbus_timeout, void *data) {
if (!dbus_timeout_get_enabled(dbus_timeout)) return TRUE;
boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
boost::asio::steady_timer &timer = *new boost::asio::steady_timer(io);
dbus_timeout_set_data(dbus_timeout, &timer, NULL);
timeout_toggled(dbus_timeout, &io);
return TRUE;
}
static void remove_timeout(DBusTimeout *dbus_timeout, void *data) {
delete static_cast<boost::asio::steady_timer *>(dbus_timeout_get_data(dbus_timeout));
}
struct dispatch_handler {
boost::asio::io_service &io;
DBusConnection *conn;
dispatch_handler(boost::asio::io_service &i, DBusConnection *c)
: io(i), conn(c) {}
void operator()() {
if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS)
io.post(dispatch_handler(io, conn));
}
};
static void dispatch_status(DBusConnection *conn, DBusDispatchStatus new_status,
void *data) {
boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
if (new_status == DBUS_DISPATCH_DATA_REMAINS)
io.post(dispatch_handler(io, conn));
}
static void set_watch_timeout_dispatch_functions(DBusConnection *conn,
boost::asio::io_service &io) {
dbus_connection_set_watch_functions(conn, &add_watch, &remove_watch,
&watch_toggled, &io, NULL);
dbus_connection_set_timeout_functions(conn, &add_timeout, &remove_timeout,
&timeout_toggled, &io, NULL);
dbus_connection_set_dispatch_status_function(conn, &dispatch_status, &io,
NULL);
}
} // namespace detail
} // namespace dbus
#endif // DBUS_WATCH_TIMEOUT_HPP