#include <functional>
#include <limits>
+using namespace DBus;
+
#define PRINT_ERROR_AND_FREE(error) \
do { \
if(error) { \
#define ADD_INTROSPECTION_FUNCTIONS(TYPE, NAME) \
template <> const std::string InterfaceNameFromType<Atspi ## TYPE>::interfaceName = \
ATSPI_DBUS_INTERFACE_ ## NAME; \
- static std::string getBusName(const std::shared_ptr<Atspi ## TYPE> &o) \
+ std::string Atspi::getBusName(const std::shared_ptr<Atspi ## TYPE> &o) \
{ \
return getBusNameImpl(o.get()); \
} \
- static std::string getPath(const std::shared_ptr<Atspi ## TYPE> &o) \
+ std::string Atspi::getPath(const std::shared_ptr<Atspi ## TYPE> &o) \
{ \
return getPathImpl(o.get()); \
} \
static void convert(std::shared_ptr<Atspi ## TYPE> &r, \
- const AtspiAccessiblePtr &obj) \
+ const AtspiAccessiblePtr &obj) \
{ \
r = { ATSPI_ ## NAME(g_object_ref(obj.get())), g_object_unref }; \
} \
- void Atspi::get ## TYPE ## Interface(const AtspiAccessiblePtr &obj, \
+ void Atspi::get ## TYPE ## Interface(const AtspiAccessiblePtr &obj, \
AsyncCallback<std::shared_ptr<Atspi ## TYPE>> callback) const \
{ \
getInterface<Atspi ## TYPE>(obj, std::move(callback)); \
{
ConnectAtClient();
- DBus proxy{"org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus"};
+ DBusClient proxy{"org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus"};
auto addr = proxy.method<std::string()>("GetAddress").call();
if (!addr) {
ERROR("failed");
} else {
- eldbusConnection = { eldbus_address_connection_get((*addr).c_str()), eldbus_connection_unref };
+ eldbusConnection = DBus::EldbusConnectionCallbackHandle{ eldbus_address_connection_get(std::get<0>(addr).c_str()), eldbus_connection_unref };
}
}
return {relationObj, g_object_unref};
}
-template <typename T> DBus Atspi::getProxy(const std::shared_ptr<T> &obj, const std::string &interface) const
+template <typename T> DBusClient Atspi::getProxy(const std::shared_ptr<T> &obj, const std::string &interface) const
{
auto bus = getBusName(obj);
auto path = getPath(obj);
- return DBus { bus, path, interface, eldbusConnection };
+ return DBusClient { bus, path, interface, eldbusConnection };
}
+template <typename T> struct function_return_type_trait {
+ using type = ValueOrError<T>;
+ using stripped_type = T;
+};
+template <typename ... T> struct function_return_type_trait<ValueOrError<T...>> {
+ using type = ValueOrError<T...>;
+};
+template <typename> struct FunctionCallbackType;
+template <typename RetType, typename ... ARGS> struct FunctionCallbackType<RetType(ARGS...)> {
+ using type = std::function<void(typename function_return_type_trait<RetType>::type)>;
+};
template <typename> struct PropertyCallback;
template <typename RetType> struct PropertyCallback<RetType(void)> {
- using VariantReturnType = EldbusVariant<RetType>;
+ using VariantReturnType = ValueOrError<EldbusVariant<RetType>>;
using RealReturnType = RetType;
- using CallType = VariantReturnType(std::string, std::string);
+ using CallType = EldbusVariant<RetType>(std::string, std::string);
};
template <typename> struct PropertySetterCallback;
template <typename ValueType> struct PropertySetterCallback<void(ValueType)> {
using RealReturnType = void;
using CallType = void(std::string, std::string, VariantSetterType);
};
-template <typename> struct returnType;
-template <typename RetType, typename ... ARGS> struct returnType<RetType(ARGS...)> {
- using type = RetType;
-};
template <typename CallType, typename InterfaceType, typename ... ARGS> void callFunction(
const EldbusConnectionCallbackHandle &eldbusConnection,
const std::string &interface,
const std::shared_ptr<InterfaceType> &obj,
- const std::string &func_name,
- typename detail::AsyncCallbackHelper<typename returnType<CallType>::type>::type callback,
+ const std::string &funcName,
+ typename FunctionCallbackType<CallType>::type callback,
ARGS &&... args
)
{
- auto bus = getBusName(obj);
- auto path = getPath(obj);
+ auto bus = Atspi::getBusName(obj);
+ auto path = Atspi::getPath(obj);
- auto dbus = DBus { bus, path, interface, eldbusConnection };
- dbus.method<CallType>(func_name).asyncCall(std::forward<ARGS>(args)..., std::move(callback));
+ auto dbus = DBusClient { bus, path, interface, eldbusConnection };
+ dbus.method<CallType>(funcName).asyncCall(std::move(callback), std::forward<ARGS>(args)...);
}
template <typename CallType, typename InterfaceType, typename ... ARGS> void callFunction(
const EldbusConnectionCallbackHandle &eldbusConnection,
const std::shared_ptr<InterfaceType> &obj,
- const std::string &func_name,
- typename detail::AsyncCallbackHelper<typename returnType<CallType>::type>::type callback,
+ const std::string &funcName,
+ typename FunctionCallbackType<CallType>::type callback,
ARGS &&... args
)
{
callFunction<CallType>(eldbusConnection, InterfaceNameFromType<InterfaceType>::interfaceName,
- obj, func_name, std::move(callback), std::forward<ARGS>(args)...);
+ obj, funcName, std::move(callback), std::forward<ARGS>(args)...);
}
template <typename CallType, typename InterfaceType, typename ... ARGS> void getProperty(
const EldbusConnectionCallbackHandle &eldbusConnection,
const std::string &interface,
const std::shared_ptr<InterfaceType> &obj,
- const std::string &func_name,
- typename detail::AsyncCallbackHelper<typename returnType<CallType>::type>::type callback,
+ const std::string &funcName,
+ typename FunctionCallbackType<CallType>::type callback,
ARGS &&... args
)
{
callFunction<typename PropertyCallback<CallType>::CallType>(
eldbusConnection, DBUS_INTERFACE_PROPERTIES, obj, "Get",
- [callback](Optional<typename PropertyCallback<CallType>::VariantReturnType> val) {
- if (!val) callback({});
- else callback(std::move((*val).value));
+ [callback](typename PropertyCallback<CallType>::VariantReturnType val) {
+ if (!val) {
+ callback(Error { val.getError() });
+ } else {
+ auto z = std::get<0>(val);
+ callback(z.value);
+ }
},
- interface, func_name);
+ interface, funcName);
}
template <typename CallType, typename InterfaceType, typename ... ARGS> void getProperty(
const EldbusConnectionCallbackHandle &eldbusConnection,
const std::shared_ptr<InterfaceType> &obj,
- const std::string &func_name,
- typename detail::AsyncCallbackHelper<typename returnType<CallType>::type>::type callback,
+ const std::string &funcName,
+ typename FunctionCallbackType<CallType>::type callback,
ARGS &&... args
)
{
getProperty<CallType>(eldbusConnection, InterfaceNameFromType<InterfaceType>::interfaceName,
- obj, func_name, std::move(callback), std::forward<ARGS>(args)...);
+ obj, funcName, std::move(callback), std::forward<ARGS>(args)...);
}
template <typename CallType, typename InterfaceType, typename Value> void setProperty(
eldbusConnection,
accessibleObj,
"GetRole",
- [callback](Optional<uint32_t> val) {
- if (!val) callback({});
- else callback((AtspiRole)*val);
- }
- );
+ [callback](ValueOrError<uint32_t> val) {
+ if (!val) callback(Error{ val.getError() });
+ else callback((AtspiRole)std::get<0>(val));
+ });
}
void Atspi::getChildrenCount(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<size_t> callback) const
eldbusConnection,
accessibleObj,
"ChildCount",
- [callback](Optional<int> val) {
- if (!val || *val < 0)
- callback({});
- else
- callback((size_t)*val);
+ [callback](ValueOrError<int> val) {
+ if (!val) callback(Error{ val.getError() });
+ else callback((size_t)std::get<0>(val));
}
);
}
eldbusConnection,
accessibleObj,
"GetState",
- [callback](Optional<std::array<uint32_t, 2>> data) {
- if (data) {
+ [callback](ValueOrError<std::array<uint32_t, 2>> data) {
+ if (!data) {
+ callback(Error{ data.getError() });
+ } else {
StateSet s;
for (size_t i = 0; i < (size_t)ATSPI_STATE_LAST_DEFINED; ++i) {
- if ((*data)[i >> 5] & (1 << (i & 31))) {
+ if (std::get<0>(data)[i >> 5] & (1 << (i & 31))) {
s[i] = true;
- assert(s[i]);
+ ASSERT(s[i]);
}
}
callback(s);
- } else {
- callback({});
}
- }
- );
+ });
}
void Atspi::getChildren(const AtspiAccessiblePtr &accessibleObj, AsyncCallback<std::vector<AtspiAccessiblePtr>> callback) const
{
getChildrenCount(accessibleObj,
- [accessibleObj, callback](Optional<size_t> childrenCount) {
+ [accessibleObj, callback](ValueOrError<size_t> childrenCount) {
if (!childrenCount) {
- callback({});
+ callback(Error{ childrenCount.getError() });
} else {
struct State {
AsyncCallback<std::vector<AtspiAccessiblePtr>> callback;
};
auto state = std::make_shared<State>();
state->callback = std::move(callback);
- state->children.resize(*childrenCount);
- state->todoCount = *childrenCount;
- for (size_t index = 0; index < *childrenCount; ++index) {
- auto cback = [state, index](Optional<AtspiAccessiblePtr> child) {
+ state->children.resize(std::get<0>(childrenCount));
+ state->todoCount = std::get<0>(childrenCount);
+ for (size_t index = 0; index < std::get<0>(childrenCount); ++index) {
+ auto cback = [state, index](ValueOrError<AtspiAccessiblePtr> child) {
if (state->failed)
return;
if (!child) {
state->failed = true;
- state->callback({});
+ state->callback(Error { child.getError() });
return;
}
- state->children[index] = std::move(*child);
- assert(state->todoCount > 0);
+ state->children[index] = std::move(std::get<0>(child));
+ ASSERT(state->todoCount > 0);
--state->todoCount;
if (state->todoCount == 0)
state->callback(std::move(state->children));
eldbusConnection,
accessibleObj,
"GetRelationSet",
- [callback](Optional<RelationSet> relationSet) {
+ [callback](ValueOrError<RelationSet> relationSet) {
if (!relationSet) {
ERROR("dbus getRelationSet failed");
- callback({});
+ callback(relationSet.getError());
return;
}
- std::vector<AccessibilityRelation> relations(relationSet->size());
- for (auto &r : *relationSet)
+ std::vector<AccessibilityRelation> relations(std::get<0>(relationSet).size());
+ for (auto &r : std::get<0>(relationSet))
relations.push_back({std::get<0>(r), std::move(std::get<1>(r))});
callback(std::move(relations));
- }
- );
+ });
}
void Atspi::getObjectInRelation(const std::shared_ptr<AtspiAccessible> &accessibleObj, AtspiRelationType searchType,
{
auto obj = accessibleObj;
getRelationSet(accessibleObj,
- [callback, obj, searchType](Optional<std::vector<AccessibilityRelation>> relations) {
+ [callback, obj, searchType](ValueOrError<std::vector<AccessibilityRelation>> relations) {
if (!relations) {
DEBUG("Relation set do not exist");
callback({});
return;
}
- DEBUG("Relations found: %d", relations->size());
- for (auto r : *relations) {
+ DEBUG("Relations found: %d", std::get<0>(relations).size());
+ for (auto r : std::get<0>(relations)) {
DEBUG("Relation type: %d", r. type);
if (r.type == searchType) {
DEBUG("Searched relation found");
namespace
{
void getValueTemplateFunction(const EldbusConnectionCallbackHandle &eldbusConnection, const std::shared_ptr<AtspiValue> &valueInterface, const std::string paramName,
- detail::AsyncCallbackHelper<double>::type callback)
+ AsyncCallback<double> callback)
{
getProperty<double()>(
eldbusConnection,
valueInterface,
paramName,
- [callback](Optional<double> value) {
- if (!value) {
- ERROR("can not get value");
- callback({});
- return;
- }
- callback(std::move(value));
- });
+ std::move(callback));
}
}
eldbusConnection,
accessibleObj,
"GetInterfaces",
- [this, callback, accessibleObj](Optional<std::vector<std::string>> val) {
+ [this, callback, accessibleObj](ValueOrError<std::vector<std::string>> val) {
const auto interfaceName = InterfaceNameFromType<T>::interfaceName;
- if (val) {
- for (auto &z : *val) {
+ if (!val) {
+ callback(Error{ val.getError() });
+ } else {
+ std::shared_ptr<T> r;
+ for (auto &z : std::get<0>(val)) {
if (z == interfaceName) {
- std::shared_ptr<T> r;
convert(r, accessibleObj);
- callback(std::move(r));
- return;
+ break;
}
}
+ callback(std::move(r));
}
- std::string interfaces;
- for (auto &z : *val) {
- if (!interfaces.empty()) interfaces += " ";
- interfaces += z;
- }
- callback({});
- }
- );
+ });
}
void Atspi::getMatchedElements(const AtspiCollectionPtr &rootObj, AtspiCollectionSortOrder sortOrder,
size_t maximumFoundElements, Matcher m, bool reverse,
AsyncCallback<std::vector<AtspiAccessiblePtr>> callback)
{
- assert(maximumFoundElements <= (size_t)std::numeric_limits<int32_t>::max());
+ ASSERT(maximumFoundElements <= (size_t)std::numeric_limits<int32_t>::max());
auto bus = getBusName(rootObj);
auto path = getPath(rootObj);
- auto pr = DBus { bus, path, ATSPI_DBUS_INTERFACE_COLLECTION, eldbusConnection };
+ auto pr = DBusClient { bus, path, ATSPI_DBUS_INTERFACE_COLLECTION, eldbusConnection };
std::get<8>(m.value) = reverse;
auto mm = pr.method<std::vector<AtspiAccessiblePtr>(decltype(Matcher::value), uint32_t, int32_t, bool)>("GetMatches");
- auto v = mm.call(
+ mm.asyncCall(std::move(callback),
std::move(m.value),
(uint32_t)sortOrder,
(int32_t)maximumFoundElements,
true);
- callback(v);
}
void Atspi::getAtPoint(Point pt, CoordType type, const AtspiAccessiblePtr &root, AsyncCallback<AtspiAccessiblePtr> callback) const
ctype = ATSPI_COORD_TYPE_WINDOW;
break;
}
- using returnType = std::tuple<AtspiAccessiblePtr, uint8_t, AtspiAccessiblePtr>;
+ using ReturnType = ValueOrError<AtspiAccessiblePtr, uint8_t, AtspiAccessiblePtr>;
struct handler {
const Atspi *atspi;
Point pt;
uint32_t ctype;
AsyncCallback<AtspiAccessiblePtr> callback;
- static void process(std::shared_ptr<handler> self, Optional<returnType> value)
+ static void process(std::shared_ptr<handler> self, ReturnType value)
{
if (!value) {
- self->callback({});
+ self->callback(Error { value.getError() });
} else {
- auto &ptr = std::get<0>(*value);
- auto recurse = std::get<1>(*value);
- auto &deputy = std::get<2>(*value);
+ auto ptr = std::get<0>(value);
+ auto recurse = std::get<1>(value);
+ auto deputy = std::get<2>(value);
if (!ptr && deputy)
ptr = std::move(deputy);
if (!recurse) {
}
return;
}
- callFunction<returnType(int32_t, int32_t, uint32_t)>(
+ callFunction<ReturnType(int32_t, int32_t, uint32_t)>(
self->atspi->eldbusConnection,
ptr,
"GetNavigableAtPoint",
- [self](Optional<returnType> value) {
+ [self](ReturnType value) {
process(std::move(self), std::move(value));
},
self->pt.x, self->pt.y, self->ctype);
}
};
auto h = std::make_shared<handler>(handler{this, pt, (uint32_t)ctype, std::move(callback)});
- callFunction<std::tuple<AtspiAccessiblePtr, uint8_t, AtspiAccessiblePtr>(int32_t, int32_t, uint32_t)>(
+ callFunction<ReturnType(int32_t, int32_t, uint32_t)>(
eldbusConnection,
root,
"GetNavigableAtPoint",
- [h](Optional<returnType> value) {
+ [h](ReturnType value) {
handler::process(std::move(h), std::move(value));
},
pt.x, pt.y, (uint32_t)ctype);
void Atspi::getAllAcceptedObjects(const AtspiAccessiblePtr &root, AsyncCallback<std::tuple<std::vector<AcceptedObjectInfo>, Rectangle>> callback) const
{
- using returnType = std::tuple<int32_t, int32_t, int32_t, int32_t,
+ using ReturnType = ValueOrError<int32_t, int32_t, int32_t, int32_t,
std::vector<std::tuple<AtspiAccessiblePtr, int32_t, int32_t, int32_t, int32_t>>>;
- callFunction<returnType()>(
+ callFunction<ReturnType()>(
eldbusConnection,
root,
"GetAllAcceptedObjects",
- [ = ](Optional<returnType> value) {
+ [ = ](ReturnType value) {
if (!value) {
- callback({});
+ callback(Error { value.getError() });
} else {
std::tuple<std::vector<AcceptedObjectInfo>, Rectangle> res;
auto &ois = std::get<0>(res);
- std::get<1>(res) = { { std::get<0>(*value), std::get<1>(*value) }, { std::get<2>(*value), std::get<3>(*value) }};
- auto &v = std::get<4>(*value);
+ std::get<1>(res) = {
+ { std::get<0>(value), std::get<1>(value) },
+ { std::get<2>(value), std::get<3>(value) }
+ };
+ auto v = std::get<4>(value);
ois.reserve(v.size());
for (auto &a : v) {
ois.push_back({});
std::vector<std::pair<std::string, std::string>> Atspi::getAttributes(const AtspiAccessiblePtr &accessibleObj) const
{
- assert(accessibleObj == root_object);
+ ASSERT(accessibleObj == root_object);
return root_object_attributes;
#if 0
// do not delete - root_* hack is temporary
#include <unordered_map>
#include <map>
#include <array>
+#include <sstream>
+#include <type_traits>
+#include <tuple>
+#include <atomic>
-namespace detail
+#define DBUS_DEBUG(...) do { DBus::debugPrint(__FILE__, __LINE__, __VA_ARGS__); } while(0)
+
+namespace DBus
{
- struct caller_eldbus_connection_unref {
- void operator()(Eldbus_Connection *p) const
- {
- eldbus_connection_unref(p);
- }
- };
+ class DBusServer;
+ class DBusClient;
+ class DBusInterfaceDescription;
- struct caller_eldbus_message_unref {
- void operator()(Eldbus_Message *p) const
- {
- eldbus_message_unref(p);
- }
- };
+ void debugPrint(const char *file, size_t line, const char *format, ...);
+ void setDebugPrinter(std::function<void(const char *, size_t)>);
- struct caller_eldbus_object_unref {
- void operator()(Eldbus_Object *p) const
- {
- eldbus_object_unref(p);
- }
- };
+ struct Error {
+ std::string message;
- struct caller_eldbus_proxy_unref {
- void operator()(Eldbus_Proxy *p) const
+ Error() = default;
+ Error(std::string msg) : message(std::move(msg))
{
- eldbus_proxy_unref(p);
+ ASSERT(!message.empty());
}
};
-}
-
-template <typename A> struct EldbusVariant {
- A value;
-};
-
-using EldbusConnectionCallbackHandle = std::shared_ptr<Eldbus_Connection>;
-using EldbusMessageCallbackHandle = std::unique_ptr<Eldbus_Message, detail::caller_eldbus_message_unref>;
-using EldbusObjectCallbackHandle = std::shared_ptr<Eldbus_Object>;
-using EldbusProxyCallbackHandle = std::shared_ptr<Eldbus_Proxy>;
-
-namespace detail
-{
- template <typename T, typename = void> struct signature;
- template <typename ... ARGS> struct signature<std::tuple<ARGS...>>;
- template <typename A, typename B> struct signature<std::pair<A, B>>;
- template <typename A> struct signature<std::vector<A>>;
- template <typename A, typename B> struct signature<std::vector<std::pair<A, B>>>;
- template <typename A, size_t N> struct signature<std::array<A, N>>;
- template <typename A, typename B, size_t N> struct signature<std::array<std::pair<A, B>, N>>;
- template <typename A, typename B> struct signature<std::unordered_map<A, B>>;
- template <typename A, typename B> struct signature<std::map<A, B>>;
+ struct Success { };
- template <typename T> struct signature<T, typename std::enable_if<std::is_enum<T>::value, void>::type> {
- static std::string sig()
+ template <typename ... ARGS> class ValueOrError
+ {
+ public:
+ ValueOrError() = default;
+ ValueOrError(ARGS ... t) : value(std::move(t)...) { }
+ ValueOrError(std::tuple<ARGS...> t) : value(std::move(t)) { }
+ ValueOrError(Error e) : error(std::move(e))
{
- return "i";
+ ASSERT(!error.message.empty());
}
- static void set(Eldbus_Message_Iter *iter, T v)
- {
- // TODO: add check for failure in marshalling arguments
- eldbus_message_iter_arguments_append(iter, sig().c_str(), (int)v);
- }
- static bool get(Eldbus_Message_Iter *iter, T &v)
+ explicit operator bool () const
{
- int q;
- auto z = eldbus_message_iter_get_and_next(iter, sig()[0], &q);
- v = (T)q;
- return z;
+ return error.message.empty();
}
- };
-
-#define SIGNATURE(T, S) \
- template <> struct signature<T> { \
- static std::string sig() { return #S; } \
- static void set(Eldbus_Message_Iter *iter, T v) { \
- eldbus_message_iter_arguments_append(iter, sig().c_str(), v); \
- } \
- static bool get(Eldbus_Message_Iter *iter, T &v) { \
- return eldbus_message_iter_get_and_next(iter, sig()[0], &v); \
- } \
- };
- SIGNATURE(uint8_t, y);
- SIGNATURE(uint16_t, q);
- SIGNATURE(uint32_t, u);
- SIGNATURE(uint64_t, t);
- SIGNATURE(int16_t, n);
- SIGNATURE(int32_t, i);
- SIGNATURE(int64_t, x);
- SIGNATURE(double, d);
-#undef SIGNATURE
-
- template <> struct signature<bool> {
- static std::string sig()
+ const Error &getError() const
{
- return "b";
+ return error;
}
- static void set(Eldbus_Message_Iter *iter, bool v)
+ std::tuple<ARGS...> &getValues()
{
- eldbus_message_iter_arguments_append(iter, sig().c_str(), v ? 1 : 0);
+ ASSERT(*this);
+ return value;
}
- static bool get(Eldbus_Message_Iter *iter, bool &v)
+ const std::tuple<ARGS...> &getValues() const
{
- unsigned char q;
- auto z = eldbus_message_iter_get_and_next(iter, sig()[0], &q);
- v = q != 0;
- return z;
+ ASSERT(*this);
+ return value;
}
+ protected:
+ std::tuple<ARGS...> value;
+ Error error;
};
- template <> struct signature<std::string> {
- static std::string sig()
- {
- return "s";
- }
- static void set(Eldbus_Message_Iter *iter, const std::string &v)
- {
- eldbus_message_iter_arguments_append(iter, sig().c_str(), v.c_str());
- }
- static void set(Eldbus_Message_Iter *iter, const char *v)
- {
- eldbus_message_iter_arguments_append(iter, sig().c_str(), v);
- }
- static bool get(Eldbus_Message_Iter *iter, std::string &v)
- {
- const char *q;
- if (!eldbus_message_iter_get_and_next(iter, 's', &q)) {
- if (!eldbus_message_iter_get_and_next(iter, 'o', &q))
- return false;
- }
- v = q;
- return true;
- }
- };
- template <size_t INDEX, typename A, typename ... ARGS> struct signature_tuple_element_type_helper {
- using type = typename signature_tuple_element_type_helper < INDEX - 1, ARGS... >::type;
- };
- template <typename A, typename ... ARGS> struct signature_tuple_element_type_helper<0, A, ARGS...> {
- using type = A;
- };
-
- template <size_t INDEX, size_t SIZE, typename ... ARGS> struct signature_tuple_helper {
- using current_type = typename signature_tuple_element_type_helper<INDEX, ARGS...>::type;
- static std::string sig()
- {
- return signature<current_type>::sig() + signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::sig();
- }
- static void set(Eldbus_Message_Iter *iter, const std::tuple<ARGS...> &args)
+ template <> class ValueOrError<>
+ {
+ public:
+ ValueOrError() = default;
+ ValueOrError(std::tuple<> t) { }
+ ValueOrError(Error e) : error(std::move(e))
{
- signature<current_type>::set(iter, std::get<INDEX>(args));
- signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::set(iter, args);
+ ASSERT(!error.message.empty());
}
- static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
+
+ explicit operator bool () const
{
- return signature<current_type>::get(iter, std::get<INDEX>(args)) &&
- signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::get(iter, args);
+ return error.message.empty();
}
- };
- template <size_t SIZE, typename ... ARGS> struct signature_tuple_helper<SIZE, SIZE, ARGS...> {
- static std::string sig()
+ const Error &getError() const
{
- return "";
+ return error;
}
- static void set(Eldbus_Message_Iter *iter, const std::tuple<ARGS...> &args)
+ std::tuple<> &getValues()
{
+ ASSERT(*this);
+ static std::tuple<> t;
+ return t;
}
- static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
+ std::tuple<> getValues() const
{
- return true;
+ ASSERT(*this);
+ return {};
}
+ protected:
+ Error error;
};
- template <typename ... ARGS> struct signature<std::tuple<ARGS...>> {
- static std::string sig()
+
+ template <> class ValueOrError<void>
+ {
+ public:
+ ValueOrError() = default;
+ ValueOrError(Success) { }
+ ValueOrError(Error e) : error(std::move(e))
{
- return "(" + signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig() + ")";
+ ASSERT(!error.message.empty());
}
- static void set(Eldbus_Message_Iter *iter, const std::tuple<ARGS...> &args)
+
+ explicit operator bool () const
{
- auto entry = eldbus_message_iter_container_new(iter, 'r', NULL);
- signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::set(entry, args);
- eldbus_message_iter_container_close(iter, entry);
+ return error.message.empty();
}
- static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
+ const Error &getError() const
{
- Eldbus_Message_Iter *entry;
- if (!eldbus_message_iter_get_and_next(iter, 'r', &entry)) return false;
- auto z = signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::get(entry, args);
- eldbus_message_iter_container_close(iter, entry);
- return z;
+ return error;
}
- };
- template <typename A, typename B> struct signature<std::pair<A, B>> {
- static std::string sig()
+ std::tuple<> &getValues()
{
- return "(" + signature_tuple_helper<0, 2, A, B>::sig() + ")";
+ ASSERT(*this);
+ static std::tuple<> t;
+ return t;
}
- static void set(Eldbus_Message_Iter *iter, const std::pair<A, B> &ab, char sg = 'r')
+ std::tuple<> getValues() const
{
- auto entry = eldbus_message_iter_container_new(iter, sg, NULL);
- signature_tuple_helper<0, 2, A, B>::set(entry, ab);
- eldbus_message_iter_container_close(iter, entry);
+ ASSERT(*this);
+ return {};
}
- static bool get(Eldbus_Message_Iter *iter, std::pair<A, B> &ab, char sg = 'r')
- {
- Eldbus_Message_Iter *entry;
- if (!eldbus_message_iter_get_and_next(iter, sg, &entry)) return false;
- std::tuple<A, B> ab_tmp;
- auto z = signature_tuple_helper<0, 2, A, B>::get(entry, ab_tmp);
- if (z) {
- ab.first = std::move(std::get<0>(ab_tmp));
- ab.second = std::move(std::get<1>(ab_tmp));
+ protected:
+ Error error;
+ };
+
+ namespace detail
+ {
+ struct caller_eldbus_connection_unref {
+ void operator()(Eldbus_Connection *p) const
+ {
+ eldbus_connection_unref(p);
}
- eldbus_message_iter_container_close(iter, entry);
- return z;
- }
+ };
+
+ struct caller_eldbus_message_unref {
+ void operator()(Eldbus_Message *p) const
+ {
+ eldbus_message_unref(p);
+ }
+ };
+
+ struct caller_eldbus_proxy_unref {
+ void operator()(Eldbus_Proxy *p) const
+ {
+ eldbus_proxy_unref(p);
+ }
+ };
+ }
+
+ template <typename A> struct EldbusVariant {
+ A value;
};
- template <typename A> struct signature<std::vector<A>> {
- static std::string sig()
- {
- return "a" + signature<A>::sig();
- }
- static void set(Eldbus_Message_Iter *iter, const std::vector<A> &v)
- {
- auto lst = eldbus_message_iter_container_new(iter, 'a', signature<A>::sig().c_str());
- ASSERT(lst);
- for (auto &a : v) {
- signature<A>::set(lst, a);
+
+ using EldbusConnectionCallbackHandle = std::shared_ptr<Eldbus_Connection>;
+ using EldbusMessageCallbackHandle = std::unique_ptr<Eldbus_Message, detail::caller_eldbus_message_unref>;
+ using EldbusObjectCallbackHandle = std::shared_ptr<Eldbus_Object>;
+
+ namespace detail
+ {
+ template <typename T, typename = void> struct signature;
+ template <typename ... ARGS> struct signature<std::tuple<ARGS...>>;
+ template <typename A, typename B> struct signature<std::pair<A, B>>;
+ template <typename A> struct signature<std::vector<A>>;
+ template <typename A, size_t N> struct signature<std::array<A, N>>;
+ template <typename A, typename B> struct signature<std::unordered_map<A, B>>;
+ template <typename A, typename B> struct signature<std::map<A, B>>;
+
+ template <typename T> struct signature<T, typename std::enable_if<std::is_enum<T>::value, void>::type> {
+ static std::string name()
+ {
+ return "enum";
}
- eldbus_message_iter_container_close(iter, lst);
- }
- static bool get(Eldbus_Message_Iter *iter, std::vector<A> &v)
- {
- Eldbus_Message_Iter *s;
- v.clear();
- if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
- A a;
- while (signature<A>::get(s, a))
- v.push_back(std::move(a));
- return true;
- }
+ static std::string sig()
+ {
+ // TODO: add check for failure in marshalling arguments
+ return "i";
+ }
+ static void set(Eldbus_Message_Iter *iter, T v)
+ {
+ eldbus_message_iter_arguments_append(iter, sig().c_str(), (int)v);
+ }
+ static bool get(Eldbus_Message_Iter *iter, T &v)
+ {
+ int q;
+ auto z = eldbus_message_iter_get_and_next(iter, sig()[0], &q);
+ v = (T)q;
+ return z;
+ }
+ };
+
+#define SIGNATURE(T, S) \
+ template <> struct signature<T> { \
+ static std::string name() { return #T; } \
+ static std::string sig() { return #S; } \
+ static void set(Eldbus_Message_Iter *iter, T v) { \
+ eldbus_message_iter_arguments_append(iter, sig().c_str(), v); \
+ } \
+ static bool get(Eldbus_Message_Iter *iter, T &v) { \
+ return eldbus_message_iter_get_and_next(iter, sig()[0], &v); \
+ } \
};
- template <typename A, size_t N> struct signature<std::array<A, N>> {
- static std::string sig()
- {
- return "a" + signature<A>::sig();
- }
- static void set(Eldbus_Message_Iter *iter, const std::array<A, N> &v)
- {
- auto lst = eldbus_message_iter_container_new(iter, 'a', signature<A>::sig().c_str());
- assert(lst);
- for (auto &a : v) {
- signature<A>::set(lst, a);
+ SIGNATURE(uint8_t, y);
+ SIGNATURE(uint16_t, q);
+ SIGNATURE(uint32_t, u);
+ SIGNATURE(uint64_t, t);
+ SIGNATURE(int16_t, n);
+ SIGNATURE(int32_t, i);
+ SIGNATURE(int64_t, x);
+ SIGNATURE(double, d);
+#undef SIGNATURE
+
+ template <> struct signature<bool> {
+ static std::string name()
+ {
+ return "bool";
}
- eldbus_message_iter_container_close(iter, lst);
- }
- static bool get(Eldbus_Message_Iter *iter, std::array<A, N> &v)
+ static std::string sig()
+ {
+ return "b";
+ }
+ static void set(Eldbus_Message_Iter *iter, bool v)
+ {
+ eldbus_message_iter_arguments_append(iter, sig().c_str(), v ? 1 : 0);
+ }
+ static bool get(Eldbus_Message_Iter *iter, bool &v)
+ {
+ unsigned char q;
+ auto z = eldbus_message_iter_get_and_next(iter, sig()[0], &q);
+ v = q != 0;
+ return z;
+ }
+ };
+ template <> struct signature<std::string> {
+ static std::string name()
+ {
+ return "string";
+ }
+ static std::string sig()
+ {
+ return "s";
+ }
+ static void set(Eldbus_Message_Iter *iter, const std::string &v)
+ {
+ eldbus_message_iter_arguments_append(iter, sig().c_str(), v.c_str());
+ }
+ static void set(Eldbus_Message_Iter *iter, const char *v)
+ {
+ eldbus_message_iter_arguments_append(iter, sig().c_str(), v);
+ }
+ static bool get(Eldbus_Message_Iter *iter, std::string &v)
+ {
+ const char *q;
+ if (!eldbus_message_iter_get_and_next(iter, 's', &q)) {
+ if (!eldbus_message_iter_get_and_next(iter, 'o', &q))
+ return false;
+ }
+ v = q;
+ return true;
+ }
+ };
+ template <> struct signature<char *> {
+ static std::string name()
+ {
+ return "string";
+ }
+ static std::string sig()
+ {
+ return "s";
+ }
+ static void set(Eldbus_Message_Iter *iter, const std::string &v)
+ {
+ eldbus_message_iter_arguments_append(iter, sig().c_str(), v.c_str());
+ }
+ static void set(Eldbus_Message_Iter *iter, const char *v)
+ {
+ eldbus_message_iter_arguments_append(iter, sig().c_str(), v);
+ }
+ };
+ template <size_t N> struct signature<char [N]> {
+ static std::string name()
+ {
+ return "string";
+ }
+ static std::string sig()
+ {
+ return "s";
+ }
+ static void set(Eldbus_Message_Iter *iter, const std::string &v)
+ {
+ eldbus_message_iter_arguments_append(iter, sig().c_str(), v.c_str());
+ }
+ static void set(Eldbus_Message_Iter *iter, const char *v)
+ {
+ eldbus_message_iter_arguments_append(iter, sig().c_str(), v);
+ }
+ };
+ template <size_t INDEX, typename A, typename ... ARGS> struct signature_tuple_element_type_helper {
+ using type = typename signature_tuple_element_type_helper < INDEX - 1, ARGS... >::type;
+ };
+ template <typename A, typename ... ARGS> struct signature_tuple_element_type_helper<0, A, ARGS...> {
+ using type = A;
+ };
+
+ template <size_t INDEX, size_t SIZE, typename ... ARGS> struct signature_tuple_helper {
+ using current_type = typename signature_tuple_element_type_helper<INDEX, ARGS...>::type;
+
+ static std::string name()
+ {
+ if (INDEX + 1 >= SIZE)
+ return signature<current_type>::name();
+ return signature<current_type>::name() + ", " + signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::name();
+ }
+ static std::string sig()
+ {
+ return signature<current_type>::sig() + signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::sig();
+ }
+ static void set(Eldbus_Message_Iter *iter, const std::tuple<ARGS...> &args)
+ {
+ signature<current_type>::set(iter, std::get<INDEX>(args));
+ signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::set(iter, args);
+ }
+ static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
+ {
+ return signature<current_type>::get(iter, std::get<INDEX>(args)) &&
+ signature_tuple_helper < INDEX + 1, SIZE, ARGS... >::get(iter, args);
+ }
+ };
+ template <size_t SIZE, typename ... ARGS> struct signature_tuple_helper<SIZE, SIZE, ARGS...> {
+ static std::string name()
+ {
+ return "";
+ }
+ static std::string sig()
+ {
+ return "";
+ }
+ static void set(Eldbus_Message_Iter *iter, const std::tuple<ARGS...> &args)
+ {
+ }
+ static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
+ {
+ return true;
+ }
+ };
+ template <typename ... ARGS> struct signature<std::tuple<ARGS...>> {
+ static std::string name()
+ {
+ return "tuple<" + signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::name() + ">";
+ }
+ static std::string sig()
+ {
+ return "(" + signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig() + ")";
+ }
+ static void set(Eldbus_Message_Iter *iter, const std::tuple<ARGS...> &args)
+ {
+ auto entry = eldbus_message_iter_container_new(iter, 'r', NULL);
+ signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::set(entry, args);
+ eldbus_message_iter_container_close(iter, entry);
+ }
+ static bool get(Eldbus_Message_Iter *iter, std::tuple<ARGS...> &args)
+ {
+ Eldbus_Message_Iter *entry;
+ if (!eldbus_message_iter_get_and_next(iter, 'r', &entry)) return false;
+ auto z = signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::get(entry, args);
+ return z;
+ }
+ };
+ template <typename ... ARGS> struct signature<ValueOrError<ARGS...>> {
+ static std::string name()
+ {
+ return "ValueOrError<" + signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::name() + ">";
+ }
+ static std::string sig()
+ {
+ return signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig();
+ }
+ static void set(Eldbus_Message_Iter *iter, const ValueOrError<ARGS...> &args)
+ {
+ signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::set(iter, args.getValues());
+ }
+ static bool get(Eldbus_Message_Iter *iter, ValueOrError<ARGS...> &args)
+ {
+ return signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::get(iter, args.getValues());
+ }
+ };
+ template <> struct signature<ValueOrError<void>> {
+ static std::string name()
+ {
+ return "";
+ }
+ static std::string sig()
+ {
+ return "";
+ }
+ static void set(Eldbus_Message_Iter *iter, const ValueOrError<void> &args)
+ {
+ }
+ static bool get(Eldbus_Message_Iter *iter, ValueOrError<void> &args)
+ {
+ return true;
+ }
+ };
+ template <typename A, typename B> struct signature<std::pair<A, B>> {
+ static std::string name()
+ {
+ return "pair<" + signature_tuple_helper<0, 2, A, B>::name() + ">";
+ }
+ static std::string sig()
+ {
+ return "(" + signature_tuple_helper<0, 2, A, B>::sig() + ")";
+ }
+ static void set(Eldbus_Message_Iter *iter, const std::pair<A, B> &ab, bool dictionary = false)
+ {
+ auto entry = eldbus_message_iter_container_new(iter, dictionary ? 'e' : 'r', NULL);
+ signature_tuple_helper<0, 2, A, B>::set(entry, ab);
+ eldbus_message_iter_container_close(iter, entry);
+ }
+ static bool get(Eldbus_Message_Iter *iter, std::pair<A, B> &ab)
+ {
+ char sg = 'r';
+ char *t = eldbus_message_iter_signature_get(iter);
+ if (t && t[0] == '{')
+ sg = '{';
+ free(t);
+
+ Eldbus_Message_Iter *entry;
+ if (!eldbus_message_iter_get_and_next(iter, sg, &entry)) return false;
+ std::tuple<A, B> ab_tmp;
+ auto z = signature_tuple_helper<0, 2, A, B>::get(entry, ab_tmp);
+ if (z) {
+ ab.first = std::move(std::get<0>(ab_tmp));
+ ab.second = std::move(std::get<1>(ab_tmp));
+ }
+ return z;
+ }
+ };
+ template <typename A> struct signature<std::vector<A>> {
+ static std::string name()
+ {
+ return "vector<" + signature<A>::name() + ">";
+ }
+ static std::string sig()
+ {
+ return "a" + signature<A>::sig();
+ }
+ static void set(Eldbus_Message_Iter *iter, const std::vector<A> &v)
+ {
+ auto lst = eldbus_message_iter_container_new(iter, 'a', signature<A>::sig().c_str());
+ ASSERT(lst);
+ for (auto &a : v) {
+ signature<A>::set(lst, a);
+ }
+ eldbus_message_iter_container_close(iter, lst);
+ }
+ static bool get(Eldbus_Message_Iter *iter, std::vector<A> &v)
+ {
+ Eldbus_Message_Iter *s;
+ v.clear();
+ if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
+ A a;
+ while (signature<A>::get(s, a))
+ v.push_back(std::move(a));
+
+ return true;
+ }
+ };
+ template <typename A, size_t N> struct signature<std::array<A, N>> {
+ static std::string name()
+ {
+ return "array<" + signature<A>::name() + ", " + std::to_string(N) + ">";
+ }
+ static std::string sig()
+ {
+ return "a" + signature<A>::sig();
+ }
+ static void set(Eldbus_Message_Iter *iter, const std::array<A, N> &v)
+ {
+ auto lst = eldbus_message_iter_container_new(iter, 'a', signature<A>::sig().c_str());
+ ASSERT(lst);
+ for (auto &a : v) {
+ signature<A>::set(lst, a);
+ }
+ eldbus_message_iter_container_close(iter, lst);
+ }
+ static bool get(Eldbus_Message_Iter *iter, std::array<A, N> &v)
+ {
+ Eldbus_Message_Iter *s;
+ if (!eldbus_message_iter_get_and_next(iter, 'a', &s))
+ return false;
+ for (auto &a : v) {
+ if (!signature<A>::get(s, a))
+ return false;
+ }
+ return true;
+ }
+ };
+ template <typename A> struct signature<EldbusVariant<A>> {
+ static std::string name()
+ {
+ return "variant<" + signature<A>::name() + ">";
+ }
+ static std::string sig()
+ {
+ return "v";
+ }
+ static void set(Eldbus_Message_Iter *iter, const EldbusVariant<A> &v)
+ {
+ set(iter, v.value);
+ }
+ static void set(Eldbus_Message_Iter *iter, const A &v)
+ {
+ auto var = eldbus_message_iter_container_new(iter, 'v', signature<A>::sig().c_str());
+ signature<A>::set(var, v);
+ eldbus_message_iter_container_close(iter, var);
+ }
+ static bool get(Eldbus_Message_Iter *iter, EldbusVariant<A> &v)
+ {
+ Eldbus_Message_Iter *s;
+ if (!eldbus_message_iter_get_and_next(iter, 'v', &s)) return false;
+ return signature<A>::get(s, v.value);
+ }
+ };
+ template <typename A, typename B> struct signature<std::unordered_map<A, B>> {
+ static std::string name()
+ {
+ return "unordered_map<" + signature<A>::name() + ", " + signature<B>::name() + ">";
+ }
+ static std::string sig()
+ {
+ return "a{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
+ }
+ static void set(Eldbus_Message_Iter *iter, const std::unordered_map<A, B> &v)
+ {
+ auto sig = "{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
+ auto lst = eldbus_message_iter_container_new(iter, 'a', sig.c_str());
+ ASSERT(lst);
+ for (auto &a : v) {
+ signature<std::pair<A, B>>::set(lst, a, true);
+ }
+ eldbus_message_iter_container_close(iter, lst);
+ }
+ static bool get(Eldbus_Message_Iter *iter, std::unordered_map<A, B> &v)
+ {
+ Eldbus_Message_Iter *s;
+ v.clear();
+ if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
+ std::pair<A, B> a;
+ while (signature<std::pair<A, B>>::get(s, a))
+ v.insert(std::move(a));
+ return true;
+ }
+ };
+ template <typename A, typename B> struct signature<std::map<A, B>> {
+ static std::string name()
+ {
+ return "map<" + signature<A>::name() + ", " + signature<B>::name() + ">";
+ }
+ static std::string sig()
+ {
+ return "a{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
+ }
+ static void set(Eldbus_Message_Iter *iter, const std::map<A, B> &v)
+ {
+ auto sig = "{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
+ auto lst = eldbus_message_iter_container_new(iter, 'a', sig.c_str());
+ ASSERT(lst);
+ for (auto &a : v) {
+ signature<std::pair<A, B>>::set(lst, a, true);
+ }
+ eldbus_message_iter_container_close(iter, lst);
+ }
+ static bool get(Eldbus_Message_Iter *iter, std::map<A, B> &v)
+ {
+ Eldbus_Message_Iter *s;
+ if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
+ std::pair<A, B> a;
+ while (signature<std::pair<A, B>>::get(s, a))
+ v.insert(std::move(a));
+ return true;
+ }
+ };
+ template <typename A> struct signature<const A &> {
+ static std::string name()
+ {
+ return "const " + signature<A>::name() + "&";
+ }
+ static std::string sig()
+ {
+ return signature<A>::sig();
+ }
+ static void set(Eldbus_Message_Iter *iter, const A &v)
+ {
+ signature<A>::set(iter, v);
+ }
+ static void get(Eldbus_Message_Iter *iter, A &v)
+ {
+ signature<A>::get(iter, v);
+ }
+ };
+ template <typename A> struct signature<A &> {
+ static std::string name()
+ {
+ return signature<A>::name() + "&";
+ }
+ static std::string sig()
+ {
+ return signature<A>::sig();
+ }
+ static void set(Eldbus_Message_Iter *iter, const A &v)
+ {
+ signature<A>::set(iter, v);
+ }
+ static void get(Eldbus_Message_Iter *iter, A &v)
+ {
+ signature<A>::get(iter, v);
+ }
+ };
+ template <typename A> struct signature<const A> {
+ static std::string name()
+ {
+ return "const " + signature<A>::name();
+ }
+ static std::string sig()
+ {
+ return signature<A>::sig();
+ }
+ static void set(Eldbus_Message_Iter *iter, const A &v)
+ {
+ signature<A>::set(iter, v);
+ }
+ static void get(Eldbus_Message_Iter *iter, A &v)
+ {
+ signature<A>::get(iter, v);
+ }
+ };
+ template <> struct signature<std::shared_ptr<AtspiAccessible>> {
+ using subtype = std::pair<std::string, std::string>;
+
+ static std::string name()
+ {
+ return "AtspiAccessiblePtr";
+ }
+ static std::string sig()
+ {
+ return "(so)";
+ }
+ static void set(Eldbus_Message_Iter *iter, const std::shared_ptr<AtspiAccessible> &v)
+ {
+ const auto prefixPath = "/org/a11y/atspi/accessible/";
+ const auto nullPath = "/org/a11y/atspi/null";
+
+ if (v) {
+ auto bus = atspi_accessible_get_bus_name(v.get(), NULL);
+ auto path = atspi_accessible_get_path(v.get(), NULL);
+ signature<subtype>::set(iter, { bus, std::string{prefixPath} + path });
+ g_free(path);
+ g_free(bus);
+ } else {
+ signature<subtype>::set(iter, { {}, std::string{nullPath} });
+ }
+ }
+ static bool get(Eldbus_Message_Iter *iter, std::shared_ptr<AtspiAccessible> &v);
+ };
+ struct CallId {
+ friend class ::DBus::DBusServer;
+ friend class ::DBus::DBusClient;
+ friend class ::DBus::DBusInterfaceDescription;
+ static std::atomic<unsigned int> LastId;
+ unsigned int id = ++LastId;
+ };
+ template <typename ValueType> ValueType unpackValues(CallId callId, const Eldbus_Message *msg)
{
- Eldbus_Message_Iter *s;
- if (!eldbus_message_iter_get_and_next(iter, 'a', &s))
- return false;
- for (auto &a : v) {
- if (!signature<A>::get(s, a))
- return false;
+ auto iter = eldbus_message_iter_get(msg);
+ ValueType r;
+
+ if (iter) {
+ if (!signature<ValueType>::get(iter, r)) {
+ DBUS_DEBUG("ValueType is %s", signature<ValueType>::name().c_str());
+ r = Error { "call " + std::to_string(callId.id) + ": failed to unpack values, got signature '" +
+ eldbus_message_signature_get(msg) + "', expected '" + signature<ValueType>::sig() + "'" };
+ }
+ } else {
+ r = Error { "call " + std::to_string(callId.id) + ": failed to get iterator" };
}
- return true;
+ return r;
}
- };
- template <typename A, typename B, size_t N> struct signature<std::array<std::pair<A, B>, N>> {
- static std::string sig()
+ inline void packValues_helper(Eldbus_Message_Iter *iter) {}
+ template <typename A, typename ...ARGS> void packValues_helper(Eldbus_Message_Iter *iter, A &&a, ARGS &&... r)
{
- return "a" + signature<std::pair<A, B>>::sig();
+ signature<A>::set(iter, std::forward<A>(a));
+ packValues_helper(iter, std::forward<ARGS>(r)...);
}
- static void set(Eldbus_Message_Iter *iter, const std::array<std::pair<A, B>, N> &v)
+ template <typename ...ARGS> void packValues(CallId callId, Eldbus_Message *msg, ARGS &&... r)
{
- auto lst = eldbus_message_iter_container_new(iter, 'a', signature<std::pair<A, B>>::sig().c_str());
- assert(lst);
- for (auto &a : v) {
- signature<std::pair<A, B>>::set(lst, a);
- }
- eldbus_message_iter_container_close(iter, lst);
+ auto iter = eldbus_message_iter_get(msg);
+ packValues_helper(iter, std::forward<ARGS>(r)...);
}
- static bool get(Eldbus_Message_Iter *iter, std::array<std::pair<A, B>, N> &v)
- {
- Eldbus_Message_Iter *s;
- char sig = 'r';
- if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
- char *t = eldbus_message_iter_signature_get(s);
- if (t && t[0] == '{')
- sig = '{';
- free(t);
- for (auto &a : v) {
- if (!signature<std::pair<A, B>>::get(s, a, sig))
- return false;
+
+ template<int ...> struct seq {};
+ template<int N, int ...S> struct gens : gens < N - 1, N - 1, S... > {};
+ template<int ...S> struct gens<0, S...> {
+ typedef seq<S...> type;
+ };
+ template <typename> struct ReturnType;
+ template <typename R, typename ... ARGS> struct ReturnType<R(ARGS...)> {
+ using type = R;
+ };
+ template <typename R, typename ... ARGS> struct ReturnType<std::function<R(ARGS...)>> {
+ using type = R;
+ };
+ template <typename C, typename ... ARGS> struct apply_helper {
+ const std::function<C> &c;
+ const std::tuple<ARGS...> &args;
+
+ template<int ... S> auto apply_2(seq<S...>)
+ {
+ return c(std::get<S>(args)...);
}
- return true;
- }
- };
- template <typename A, typename B> struct signature<std::vector<std::pair<A, B>>> {
- static std::string sig()
- {
- return "a" + signature<std::pair<A, B>>::sig();
- }
- static void set(Eldbus_Message_Iter *iter, const std::vector<std::pair<A, B>> &v)
- {
- auto lst = eldbus_message_iter_container_new(iter, 'a', signature<std::pair<A, B>>::sig().c_str());
- ASSERT(lst);
- for (auto &a : v) {
- signature<std::pair<A, B>>::set(lst, a);
+ auto apply_1()
+ {
+ return apply_2(typename gens<sizeof...(ARGS)>::type());
}
- eldbus_message_iter_container_close(iter, lst);
- }
- static bool get(Eldbus_Message_Iter *iter, std::vector<std::pair<A, B>> &v)
- {
- Eldbus_Message_Iter *s;
- v.clear();
- char sig = 'r';
- if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
- char *t = eldbus_message_iter_signature_get(s);
+ };
+ template <typename C, typename D, typename ... ARGS> struct apply_helper_2 {
+ const std::function<C> &c;
+ const D &d;
+ const std::tuple<ARGS...> &args;
- // TODO: move handling of std::pair special case involing '{' to signature<std::pair<*, *>>
- if (t && t[0] == '{')
- sig = '{';
- free(t);
- std::pair<A, B> a;
- while (signature<std::pair<A, B>>::get(s, a, sig))
- v.push_back(std::move(a));
- return true;
- }
- };
- template <typename A> struct signature<EldbusVariant<A>> {
- static std::string sig()
- {
- return "v";
- }
- static void set(Eldbus_Message_Iter *iter, const EldbusVariant<A> &v)
- {
- set(iter, v.value);
- }
- static void set(Eldbus_Message_Iter *iter, const A &v)
- {
- auto var = eldbus_message_iter_container_new(iter, 'v', signature<A>::sig().c_str());
- signature<A>::set(var, v);
- eldbus_message_iter_container_close(iter, var);
- }
- static bool get(Eldbus_Message_Iter *iter, EldbusVariant<A> &v)
- {
- Eldbus_Message_Iter *s;
- if (!eldbus_message_iter_get_and_next(iter, 'v', &s)) return false;
- return signature<A>::get(s, v.value);
- }
- };
- template <typename A, typename B> struct signature<std::unordered_map<A, B>> {
- static std::string sig()
- {
- return "a{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
- }
- static void set(Eldbus_Message_Iter *iter, const std::unordered_map<A, B> &v)
- {
- auto sig = "{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
- auto lst = eldbus_message_iter_container_new(iter, 'a', sig.c_str());
- assert(lst);
- for (auto &a : v) {
- signature<std::pair<A, B>>::set(lst, a, 'e');
+ template<int ... S> auto apply_2(seq<S...>)
+ {
+ return c(d, std::get<S>(args)...);
}
- eldbus_message_iter_container_close(iter, lst);
- }
- static bool get(Eldbus_Message_Iter *iter, std::unordered_map<A, B> &v)
- {
- Eldbus_Message_Iter *s;
- v.clear();
- if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
- std::pair<A, B> a;
- while (signature<std::pair<A, B>>::get(s, a, '{'))
- v.insert(std::move(a));
- return true;
- }
- };
- template <typename A, typename B> struct signature<std::map<A, B>> {
- static std::string sig()
- {
- return "a{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
- }
- static void set(Eldbus_Message_Iter *iter, const std::map<A, B> &v)
- {
- auto sig = "{" + signature_tuple_helper<0, 2, A, B>::sig() + "}";
- auto lst = eldbus_message_iter_container_new(iter, 'a', sig.c_str());
- assert(lst);
- for (auto &a : v) {
- signature<std::pair<A, B>>::set(lst, a, 'e');
+ auto apply_1()
+ {
+ return apply_2(typename gens<sizeof...(ARGS)>::type());
}
- eldbus_message_iter_container_close(iter, lst);
- }
- static bool get(Eldbus_Message_Iter *iter, std::map<A, B> &v)
- {
- Eldbus_Message_Iter *s;
- v.clear();
- if (!eldbus_message_iter_get_and_next(iter, 'a', &s)) return false;
- std::pair<A, B> a;
- while (signature<std::pair<A, B>>::get(s, a, '{'))
- v.insert(std::move(a));
- return true;
- }
- };
- template <typename A> struct signature<const A &> {
- static std::string sig()
+ };
+ template <typename C, typename ... ARGS> auto apply(const std::function<C> &c, const std::tuple<ARGS...> &args) -> typename ReturnType<C>::type
{
- return signature<A>::sig();
+ apply_helper<C, ARGS...> ah { c, args };
+ return ah.apply_1();
}
- static void set(Eldbus_Message_Iter *iter, const A &v)
+ template <typename C, typename D, typename ... ARGS> auto apply(const std::function<C> &c, const D &d, const std::tuple<ARGS...> &args) -> typename ReturnType<C>::type
{
- signature<A>::set(iter, v);
+ apply_helper_2<C, D, ARGS...> ah { c, d, args };
+ return ah.apply_1();
}
- static void get(Eldbus_Message_Iter *iter, A &v)
+
+ struct EldbusProxyBase {
+ EldbusProxyBase()
+ {
+ eldbus_init();
+ }
+ ~EldbusProxyBase()
+ {
+ eldbus_shutdown();
+ }
+ };
+
+ constexpr static int ELDBUS_CALL_TIMEOUT = 1000;
+
+ struct ConnectionState {
+ EldbusConnectionCallbackHandle connection;
+ EldbusObjectCallbackHandle object;
+ Eldbus_Proxy *proxy = nullptr; // proxy lives only as long as object
+ Eldbus_Proxy *propertiesProxy = nullptr;
+ };
+ static void callAsyncCb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
- signature<A>::get(iter, v);
+ auto d = (std::pair<CallId, std::function<void(const Eldbus_Message *)>> *)data;
+ DBUS_DEBUG("call %d: got reply", d->first.id);
+ d->second(msg);
}
- };
- template <typename A> struct signature<A &> {
- static std::string sig()
+ static void pendingFreeCb(void *data, const void *)
{
- return signature<A>::sig();
+ auto d = (std::pair<CallId, std::function<void(const Eldbus_Message *)>> *)data;
+ DBUS_DEBUG("call %d: deleting", d->first.id);
+ delete d;
}
- static void set(Eldbus_Message_Iter *iter, const A &v)
+ template <typename RETTYPE, typename ... ARGS>
+ RETTYPE call(CallId callId, ConnectionState &connectionState, bool property, const std::string &funcName, const ARGS &... args)
{
- signature<A>::set(iter, v);
+ auto proxy = property ? connectionState.propertiesProxy : connectionState.proxy;
+ if (!proxy) {
+ DBUS_DEBUG("call %d: not initialized", callId.id);
+ return Error { "not initialized" };
+ }
+
+ DBUS_DEBUG("call %d: calling '%s'", callId.id, funcName.c_str());
+ EldbusMessageCallbackHandle msg{eldbus_proxy_method_call_new(proxy, funcName.c_str())};
+ if (!msg) {
+ DBUS_DEBUG("call %d: failed", callId.id);
+ return Error { "failed to create message" };
+ }
+
+ detail::packValues(callId, msg.get(), args...);
+ auto replyRawPtr = eldbus_proxy_send_and_block(proxy, msg.release(), ELDBUS_CALL_TIMEOUT);
+ EldbusMessageCallbackHandle reply{ replyRawPtr };
+ DBUS_DEBUG("call %d: calling '%s' done", callId.id, funcName.c_str());
+ if (!reply) {
+ DBUS_DEBUG("call %d: failed", callId.id);
+ return Error { "eldbus returned null as reply" };
+ }
+ const char *errname, *errmsg;
+ if (eldbus_message_error_get(reply.get(), &errname, &errmsg)) {
+ DBUS_DEBUG("call %d: %s: %s", callId.id, errname, errmsg);
+ return Error { std::string(errname) + ": " + errmsg };
+ }
+ DBUS_DEBUG("call %d: got reply with signature '%s'", callId.id, eldbus_message_signature_get(reply.get()));
+ return detail::unpackValues<RETTYPE>(callId, reply.get());
}
- static void get(Eldbus_Message_Iter *iter, A &v)
- {
- signature<A>::get(iter, v);
+
+ template <typename RETTYPE, typename ... ARGS>
+ void asyncCall(CallId callId, ConnectionState connectionState, bool property, const std::string &funcName,
+ std::function<void(RETTYPE)> callback, const ARGS &... args)
+ {
+ auto proxy = property ? connectionState.propertiesProxy : connectionState.proxy;
+ if (!proxy) {
+ DBUS_DEBUG("call %d: not initialized", callId.id);
+ callback(Error { "not initialized" });
+ return;
+ }
+
+ EldbusMessageCallbackHandle msg{eldbus_proxy_method_call_new(proxy, funcName.c_str())};
+ if (!msg) {
+ DBUS_DEBUG("call %d: failed", callId.id);
+ callback(Error { "failed to create message" });
+ return;
+ }
+
+ auto cbData = new std::pair<CallId, std::function<void(const Eldbus_Message *)>> {callId,
+ [callback, connectionState, callId](const Eldbus_Message * reply)
+ {
+ DBUS_DEBUG("call %d: calling done", callId.id);
+ if (!reply) {
+ DBUS_DEBUG("call %d: failed", callId.id);
+ callback(Error { "eldbus returned null as reply" });
+ } else {
+ const char *errname, *errmsg;
+ if (eldbus_message_error_get(reply, &errname, &errmsg)) {
+ DBUS_DEBUG("call %d: %s: %s", callId.id, errname, errmsg);
+ callback(Error { std::string(errname) + ": " + errmsg });
+ } else {
+ DBUS_DEBUG("call %d: got reply with signature '%s'", callId.id, eldbus_message_signature_get(reply));
+ callback(detail::unpackValues<RETTYPE>(callId, reply));
+ }
+ }
+ }
+ };
+ detail::packValues(callId, msg.get(), args...);
+ auto pending = eldbus_proxy_send(proxy, msg.release(), callAsyncCb, cbData, ELDBUS_CALL_TIMEOUT);
+ if (pending) {
+ eldbus_pending_free_cb_add(pending, pendingFreeCb, cbData);
+ DBUS_DEBUG("call %d: call sent", callId.id);
+ } else {
+ DBUS_DEBUG("call %d: failed to send call", callId.id);
+ callback(Error { "failed to send call" });
+ }
}
- };
- template <typename A> struct signature<const A> {
- static std::string sig()
+ inline void displayDebugCallInfo(CallId callId, const std::string &funcName, const std::string &info, const std::string &interfaceName)
{
- return signature<A>::sig();
+ DBUS_DEBUG("call %d: %s iname = %s fname = %s", callId.id, info.c_str(), interfaceName.c_str(), funcName.c_str());
}
- static void set(Eldbus_Message_Iter *iter, const A &v)
+ inline void displayDebugCallInfoSignal(CallId callId, const std::string &funcName, const std::string &info, const std::string &interfaceName)
{
- signature<A>::set(iter, v);
+ DBUS_DEBUG("call %d: %s signal iname = %s fname = %s", callId.id, info.c_str(), interfaceName.c_str(), funcName.c_str());
}
- static void get(Eldbus_Message_Iter *iter, A &v)
+ inline void displayDebugCallInfoProperty(CallId callId, const std::string &funcName, std::string info, const std::string &interfaceName,
+ const std::string &propertyName)
{
- signature<A>::get(iter, v);
+ DBUS_DEBUG("call %d: %s iname = %s pname = %s", callId.id, info.c_str(), interfaceName.c_str(), propertyName.c_str());
}
- };
- template <> struct signature<std::shared_ptr<AtspiAccessible>> {
- using subtype = std::pair<std::string, std::string>;
- static std::string sig()
- {
- return "(so)";
- }
- static void set(Eldbus_Message_Iter *iter, const std::shared_ptr<AtspiAccessible> &v)
+ class StringStorage
{
- const auto prefixPath = "/org/a11y/atspi/accessible/";
- const auto nullPath = "/org/a11y/atspi/null";
-
- if (v) {
- auto bus = atspi_accessible_get_bus_name(v.get(), NULL);
- auto path = atspi_accessible_get_path(v.get(), NULL);
- signature<subtype>::set(iter, { bus, std::string{prefixPath} + path });
- g_free(path);
- g_free(bus);
- } else {
- signature<subtype>::set(iter, { {}, std::string{nullPath} });
+ struct char_ptr_deleter {
+ void operator()(char *p)
+ {
+ free(p);
+ }
+ };
+ std::vector<std::unique_ptr<char, char_ptr_deleter>> storage;
+ public:
+ const char *add(const char *txt)
+ {
+ auto ptr = strdup(txt);
+ storage.push_back(std::unique_ptr<char, char_ptr_deleter>(ptr));
+ return storage.back().get();
}
- }
- static bool get(Eldbus_Message_Iter *iter, std::shared_ptr<AtspiAccessible> &v);
- };
- struct CallId {
- static unsigned int LastId;
- unsigned int id = ++LastId;
- };
- template <typename R> struct unpackValues_Helper {
- bool operator()(CallId callId, R &r, const Eldbus_Message *msg) const
- {
- auto iter = eldbus_message_iter_get(msg);
+ const char *add(const std::string &txt)
+ {
+ return add(txt.c_str());
+ }
+ };
+ template <typename A, typename ... ARGS> struct EldbusArgGenerator_Helper {
+ static void add(std::vector<Eldbus_Arg_Info> &r, StringStorage &strings)
+ {
+ auto s = r.size();
+ auto sig = signature<A>::sig();
+ ASSERT(!sig.empty());
+ auto name = "p" + std::to_string(s + 1);
+ r.push_back(Eldbus_Arg_Info{ strings.add(sig), strings.add(name) });
+ EldbusArgGenerator_Helper<ARGS...>::add(r, strings);
+ }
+ };
+ template <> struct EldbusArgGenerator_Helper<void> {
+ static void add(std::vector<Eldbus_Arg_Info> &, StringStorage &)
+ {
+ }
+ };
+ template <typename ... ARGS> struct EldbusArgGenerator_Helper<std::tuple<ARGS...>> {
+ static void add(std::vector<Eldbus_Arg_Info> &r, StringStorage &strings)
+ {
+ EldbusArgGenerator_Helper<ARGS..., void>::add(r, strings);
+ }
+ };
+ template <typename RetType> struct dbus_interface_return_type_traits {
+ using type = ValueOrError<RetType>;
+ };
+ template <typename ... ARGS> struct dbus_interface_return_type_traits<ValueOrError<ARGS...>> {
+ using type = ValueOrError<ARGS...>;
+ };
+ template <typename T> struct dbus_interface_traits;
+ template <typename RetType, typename ... ARGS> struct dbus_interface_traits<RetType(ARGS...)> {
+ using Ret = typename dbus_interface_return_type_traits<RetType>::type;
+ using SyncCB = std::function<Ret(ARGS...)>;
+ using AsyncCB = std::function<void(std::function<void(Ret)>, ARGS...)>;
+ using VEArgs = ValueOrError<ARGS...>;
+ };
+ template <typename T> struct EldbusArgGenerator_Args;
+ template <typename RetType, typename ... ARGS> struct EldbusArgGenerator_Args<RetType(ARGS...)> {
+ static auto name()
+ {
+ return signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::name();
+ }
+ static auto get(StringStorage &strings)
+ {
+ std::vector<Eldbus_Arg_Info> tmp;
+ EldbusArgGenerator_Helper<ARGS..., void>::add(tmp, strings);
+ tmp.push_back({ nullptr, nullptr });
+ return tmp;
+ }
+ };
+ template <typename T> struct EldbusArgGenerator_ReturnType;
+ template <typename RetType, typename ... ARGS> struct EldbusArgGenerator_ReturnType<RetType(ARGS...)> {
+ static std::string name()
+ {
+ return signature<RetType>::name();
+ }
+ static auto get(StringStorage &strings)
+ {
+ std::vector<Eldbus_Arg_Info> tmp;
+ EldbusArgGenerator_Helper<RetType, void>::add(tmp, strings);
+ tmp.push_back({ nullptr, nullptr });
+ return tmp;
+ }
+ };
+ template <typename T> struct EldbusArgGenerator_ReturnType;
+ template <typename ... ARGS> struct EldbusArgGenerator_ReturnType<void(ARGS...)> {
+ static std::string name()
+ {
+ return "";
+ }
+ static auto get(StringStorage &strings)
+ {
+ std::vector<Eldbus_Arg_Info> tmp;
+ tmp.push_back({ nullptr, nullptr });
+ return tmp;
+ }
+ };
+ }
- if (iter) {
- if (signature<R>::get(iter, r))
- return true;
- ERROR("call %d: signature mismatch, got %s, expected %s",
- callId.id,
- eldbus_message_signature_get(msg),
- signature<R>::sig().c_str());
- } else
- ERROR("call %d: failed to get iter", callId.id);
- return false;
- }
- };
- template <typename ...ARGS> struct unpackValues_Helper<std::tuple<ARGS...>> {
- bool operator()(CallId callId, std::tuple<ARGS...> &r, const Eldbus_Message *msg) const
- {
- auto iter = eldbus_message_iter_get(msg);
- if (iter) {
- if (signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::get(iter, r))
- return true;
- ERROR("call %d: signature mismatch, got %s, expected %s",
- callId.id,
- eldbus_message_signature_get(msg),
- signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig().c_str());
- } else
- ERROR("call %d: failed to get iter", callId.id);
- return false;
- }
+ enum class ConnectionType {
+ SYSTEM, SESSION
};
- template <typename R> bool unpackValues(CallId callId, R &r, const Eldbus_Message *msg)
- {
- return unpackValues_Helper<R>()(callId, r, msg);
- }
- template <typename R> void packValues(CallId callId, Eldbus_Message *msg, const R &r)
- {
- DEBUG("call %d: packing values with signature %s", callId.id, signature<R>::sig().c_str());
- auto iter = eldbus_message_iter_get(msg);
- signature<R>::set(iter, r);
- DEBUG("call %d: signature is %s", callId.id, eldbus_message_signature_get(msg));
- }
- inline void packValues_helper(Eldbus_Message_Iter *iter) {}
- template <typename A, typename ...ARGS> void packValues_helper(Eldbus_Message_Iter *iter, A &&a, ARGS &&... r)
- {
- signature<A>::set(iter, std::forward<A>(a));
- packValues_helper(iter, std::forward<ARGS>(r)...);
- }
- template <typename ...ARGS> void packValues(CallId callId, Eldbus_Message *msg, ARGS &&... r)
+
+ class DBusClient : private detail::EldbusProxyBase
{
- DEBUG("call %d: packing values with signature %s", callId.id, signature_tuple_helper<0, sizeof...(ARGS), ARGS...>::sig().c_str());
- auto iter = eldbus_message_iter_get(msg);
- packValues_helper(iter, std::forward<ARGS>(r)...);
- DEBUG("call %d: signature is %s", callId.id, eldbus_message_signature_get(msg));
- }
+ public:
+ DBusClient() = default;
+ DBusClient(std::string bus_name, std::string path_name, std::string interface_name,
+ ConnectionType tp);
+ DBusClient(std::string bus_name, std::string path_name, std::string interface_name,
+ const EldbusConnectionCallbackHandle &conn = {});
+ ~DBusClient();
- template<int ...> struct seq {};
- template<int N, int ...S> struct gens : gens < N - 1, N - 1, S... > {};
- template<int ...S> struct gens<0, S...> {
- typedef seq<S...> type;
- };
+ DBusClient(const DBusClient &) = delete;
+ DBusClient(DBusClient &&) = default;
- template <typename C, typename R, typename ... ARGS> struct apply_helper {
- const C &c;
- const std::tuple<ARGS...> &args;
+ DBusClient &operator = (DBusClient &&) = default;
+ DBusClient &operator = (const DBusClient &) = delete;
- template<int ... S> R apply_2(seq<S...>)
+ explicit operator bool () const
{
- return c(std::get<S>(args)...);
+ return bool(connectionState.proxy);
}
- R apply_1()
- {
- return apply_2(typename gens<sizeof...(ARGS)>::type());
- }
- };
- template <typename C, typename R, typename ... ARGS> R apply(const C &c, const std::tuple<ARGS...> &args)
- {
- apply_helper<C, R, ARGS...> ah { c, args };
- return ah.apply_1();
- }
- struct EldbusProxyBase {
- EldbusProxyBase()
+ template <typename T> struct Method {
+ using RetType = typename detail::dbus_interface_traits<T>::Ret;
+ detail::ConnectionState connectionState;
+ std::string funcName, interfaceName;
+ std::string info;
+
+ template <typename ... ARGS> RetType call(const ARGS &... args)
+ {
+ detail::CallId callId;
+ detail::displayDebugCallInfo(callId, funcName, info, interfaceName);
+ return detail::call<RetType>(callId, connectionState, false, funcName, args...);
+ }
+ template <typename ... ARGS> void asyncCall(std::function<void(RetType)> callback, const ARGS &... args)
+ {
+ detail::CallId callId;
+ detail::displayDebugCallInfo(callId, funcName, info, interfaceName);
+ auto connectionState = this->connectionState;
+ detail::asyncCall<RetType>(callId, connectionState, false, funcName, std::move(callback), args...);
+ }
+ };
+ template <typename T> struct Property {
+ using RetType = typename detail::dbus_interface_return_type_traits<T>::type;
+ using VariantRetType = typename detail::dbus_interface_return_type_traits<EldbusVariant<T>>::type;
+ detail::ConnectionState connectionState;
+ std::string propName, interfaceName;
+ std::string info;
+
+ RetType get()
+ {
+ detail::CallId callId;
+ detail::displayDebugCallInfoProperty(callId, "Get", info, interfaceName, propName);
+ auto z = detail::call<VariantRetType>(callId, connectionState, true, "Get", interfaceName, propName);
+ if (!z) return z.getError();
+ return { std::get<0>(z.getValues()).value };
+ }
+ void asyncGet(std::function<void(RetType)> callback)
+ {
+ detail::CallId callId;
+ detail::displayDebugCallInfoProperty(callId, "Get", info, interfaceName, propName);
+ auto connectionState = this->connectionState;
+ auto cc = [callback](VariantRetType reply) {
+ if (reply)
+ callback(std::move(std::get<0>(reply.getValues()).value));
+ else
+ callback(reply.getError());
+ };
+ detail::asyncCall<VariantRetType>(callId, connectionState, true, "Get", std::move(cc), interfaceName, propName);
+ }
+ ValueOrError<void> set(const T &r)
+ {
+ detail::CallId callId;
+ detail::displayDebugCallInfoProperty(callId, "Set", info, interfaceName, propName);
+ EldbusVariant<T> variantValue { std::move(r) };
+ return detail::call<ValueOrError<void>>(callId, connectionState, true, "Set", interfaceName, propName, variantValue);
+ }
+ void asyncSet(std::function<void(ValueOrError<void>)> callback, const T &r)
+ {
+ detail::CallId callId;
+ detail::displayDebugCallInfoProperty(callId, "Set", info, interfaceName, propName);
+ EldbusVariant<T> variantValue { std::move(r) };
+ detail::asyncCall<ValueOrError<void>>(callId, connectionState, true, "Set", std::move(callback), interfaceName, propName, variantValue);
+ }
+ };
+
+ template <typename PropertyType>
+ auto property(std::string propName)
{
- eldbus_init();
+ return Property<PropertyType> { connectionState, std::move(propName), interfaceName, info };
}
- ~EldbusProxyBase()
+ template <typename MethodType>
+ auto method(std::string funcName)
{
- eldbus_shutdown();
+ return Method<MethodType> { connectionState, std::move(funcName), interfaceName, info };
}
- };
-}
-namespace detail
-{
- template <typename Q> struct Method;
- template <typename R, typename ... ARGS> struct Method<R(ARGS...)>;
- template <typename ... ARGS> struct Method<void(ARGS...)>;
- template <typename Q> struct Listen;
- template <typename ... ARGS> struct Listen<void(ARGS...)>;
-
- constexpr static int ELDBUS_CALL_TIMEOUT = 1000;
+ template <typename SignalType> void addSignal(std::string signalName, std::function<SignalType> callback)
+ {
+ detail::CallId callId;
+ detail::displayDebugCallInfoSignal(callId, signalName, info, interfaceName);
+ auto tmp = new std::function<void(const Eldbus_Message *msg)> {
+ [ callId, connectionState = this->connectionState, callback ](const Eldbus_Message * msg) -> void {
+ const char *errname, *aux;
+ if (eldbus_message_error_get(msg, &errname, &aux))
+ {
+ DBUS_DEBUG("call %d: Eldbus error: %s %s", callId.id, errname, aux);
+ return;
+ }
- struct ConnectionState {
- EldbusConnectionCallbackHandle connection;
- EldbusObjectCallbackHandle object;
- EldbusProxyCallbackHandle proxy;
- };
- static void callAsyncCb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
- {
- auto d = (std::pair<CallId, std::function<void(const Eldbus_Message *)>> *)data;
- DEBUG("call %d: got reply", d->first.id);
- d->second(msg);
- }
- static void pendingFreeCb(void *data, const void *)
- {
- auto d = (std::pair<CallId, std::function<void(const Eldbus_Message *)>> *)data;
- DEBUG("call %d: deleting", d->first.id);
- delete d;
- }
- template <typename ... ARGS>
- EldbusMessageCallbackHandle call(CallId callId, ConnectionState &connectionState, const std::string &func_name, const ARGS &... args)
- {
- if (!connectionState.proxy) {
- DEBUG("call %d: not initialized", callId.id);
- return {};
+ DBUS_DEBUG("call %d: received signal with signature '%s'", callId.id, eldbus_message_signature_get(msg));
+ using ParamsType = typename detail::dbus_interface_traits<SignalType>::VEArgs;
+ auto params = detail::unpackValues<ParamsType>(callId, msg);
+ if (!params)
+ {
+ DBUS_DEBUG("call %d: failed: %s", callId.id, params.getError().message.c_str());
+ return;
+ }
+ detail::apply(callback, params.getValues());
+ }
+ };
+ eldbus_proxy_signal_handler_add(connectionState.proxy, signalName.c_str(), listenerCallback, tmp);
+ destructors.push_back([ = ]() {
+ delete tmp;
+ });
}
- DEBUG("call %d: calling '%s'", callId.id, func_name.c_str());
- EldbusMessageCallbackHandle msg{eldbus_proxy_method_call_new(connectionState.proxy.get(), func_name.c_str())};
- if (!msg) {
- DEBUG("call %d: failed", callId.id);
- return {};
- }
+ private:
+ detail::ConnectionState connectionState;
+ std::vector<std::function<void()>> destructors;
+ std::string info, interfaceName;
- detail::packValues(callId, msg.get(), args...);
- auto replyRawPtr = eldbus_proxy_send_and_block(connectionState.proxy.get(), msg.release(), ELDBUS_CALL_TIMEOUT);
- EldbusMessageCallbackHandle reply{ replyRawPtr };
- DEBUG("call %d: calling '%s' done", callId.id, func_name.c_str());
- if (!reply) {
- DEBUG("call %d: failed", callId.id);
- return {};
- }
- const char *errname, *errmsg;
- if (eldbus_message_error_get(reply.get(), &errname, &errmsg)) {
- DEBUG("call %d: %s: %s", callId.id, errname, errmsg);
- return {};
+ static void listenerCallback(void *data, const Eldbus_Message *msg)
+ {
+ (*((std::function<void(const Eldbus_Message *msg)> *)data))(msg);
}
- DEBUG("call %d: got reply with signature '%s'", callId.id, eldbus_message_signature_get(reply.get()));
- return reply;
- }
+ };
- template <typename ... ARGS>
- void asyncCall(CallId callId, ConnectionState connectionState, const std::string &func_name,
- std::function<void(const Eldbus_Message *)> callback, const ARGS &... args)
+ class DBusInterfaceDescription
{
- if (!connectionState.proxy) {
- DEBUG("call %d: not initialized", callId.id);
- callback({});
- return;
- }
+ friend class DBusServer;
+ public:
+ enum class type {
+ method, property
+ };
+ struct MethodInfo {
+ detail::CallId id;
+ std::string memberName;
+ std::vector<Eldbus_Arg_Info> in, out;
+ std::function<Eldbus_Message *(const Eldbus_Message *msg)> callback;
+ };
+ struct SignalInfo {
+ detail::CallId id;
+ std::string memberName;
+ std::vector<Eldbus_Arg_Info> args;
+ unsigned int uniqueId;
+ };
+ struct PropertyInfo {
+ detail::CallId setterId, getterId;
+ std::string memberName, typeSignature;
+ std::function<ValueOrError<void>(const Eldbus_Message *src, Eldbus_Message_Iter *dst)> getCallback, setCallback;
+ };
+ class SignalId
+ {
+ friend class ::DBus::DBusServer;
+ friend class ::DBus::DBusClient;
+ friend class ::DBus::DBusInterfaceDescription;
+ detail::CallId id;
- EldbusMessageCallbackHandle msg{eldbus_proxy_method_call_new(connectionState.proxy.get(), func_name.c_str())};
- if (!msg) {
- DEBUG("call %d: failed", callId.id);
- callback({});
- return;
- }
+ SignalId() = default;
+ SignalId(detail::CallId id) : id(id) { }
+ };
+ DBusInterfaceDescription(std::string interfaceName);
- auto cbData = new std::pair<CallId, std::function<void(const Eldbus_Message *)>> {
- callId, [callback, connectionState, callId](const Eldbus_Message * reply)
- {
- DEBUG("call %d: calling done", callId.id);
- if (!reply)
- DEBUG("call %d: failed", callId.id);
- else {
- const char *errname, *errmsg;
- if (eldbus_message_error_get(reply, &errname, &errmsg)) {
- DEBUG("call %d: %s: %s", callId.id, errname, errmsg);
- reply = nullptr;
- } else
- DEBUG("call %d: got reply with signature '%s'", callId.id, eldbus_message_signature_get(reply));
- }
- callback(reply);
+ template <typename T> void addMethod(const std::string &memberName, typename detail::dbus_interface_traits<T>::SyncCB callback)
+ {
+ detail::CallId callId;
+ MethodInfo mi;
+ methods.push_back(std::move(mi));
+ auto &z = methods.back();
+ z.in = detail::EldbusArgGenerator_Args<T>::get(strings);
+ z.out = detail::EldbusArgGenerator_ReturnType<T>::get(strings);
+ z.memberName = memberName;
+ DBUS_DEBUG("call %d: method %s, in %s, out %s", callId.id, memberName.c_str(),
+ detail::EldbusArgGenerator_Args<T>::name().c_str(),
+ detail::EldbusArgGenerator_ReturnType<T>::name().c_str()
+ );
+ z.callback = construct<T>(callId, callback);
+ z.id = callId;
+ }
+ template <typename T> void addAsyncMethod(const std::string &memberName, typename detail::dbus_interface_traits<T>::AsyncCB callback);
+ template <typename T> void addProperty(const std::string &memberName, std::function<ValueOrError<T>()> getter, std::function<ValueOrError<void>(T)> setter)
+ {
+ properties.push_back({});
+ auto &z = properties.back();
+ z.memberName = memberName;
+ z.typeSignature = detail::signature<T>::sig();
+ if (getter) {
+ detail::CallId getterId;
+ z.getterId = getterId;
+ DBUS_DEBUG("call %d: property %s (get) type %s", getterId.id, memberName.c_str(), detail::signature<T>::name().c_str());
+ z.getCallback = [ = ](const Eldbus_Message * src, Eldbus_Message_Iter * dst) -> ValueOrError<void> {
+ auto v = detail::apply(getter, std::tuple<>{});
+ if (v)
+ {
+ detail::signature<T>::set(dst, std::get<0>(v.getValues()));
+ DBUS_DEBUG("call %d: success", getterId.id);
+ return Success{};
+ }
+ DBUS_DEBUG("call %d: failed: %s", getterId.id, v.getError().message.c_str());
+ return v.getError();
+ };
+ }
+ if (setter) {
+ detail::CallId setterId;
+ z.setterId = setterId;
+ DBUS_DEBUG("call %d: property %s (set) type %s", setterId.id, memberName.c_str(), detail::signature<T>::name().c_str());
+ z.setCallback = [ = ](const Eldbus_Message * src, Eldbus_Message_Iter * src_iter) -> ValueOrError<void> {
+ std::tuple<T> value;
+ auto src_signature = eldbus_message_iter_signature_get(src_iter);
+ if (detail::signature<T>::get(src_iter, std::get<0>(value)))
+ {
+ auto v = detail::apply(setter, std::move(value));
+ if (v) {
+ DBUS_DEBUG("call %d: success", setterId.id);
+ return Success{};
+ }
+ DBUS_DEBUG("call %d: failed: %s", setterId.id, v.getError().message.c_str());
+ free(src_signature);
+ return v.getError();
+ }
+ DBUS_DEBUG("call %d: failed to unpack values, got signature '%s', expected '%s'", setterId.id,
+ src_signature, detail::signature<T>::sig().c_str());
+ return Error { "call " + std::to_string(setterId.id) + ": failed to unpack values, got signature '" +
+ src_signature + "', expected '" + detail::signature<T>::sig() + "'" };
+ };
}
- };
- detail::packValues(callId, msg.get(), args...);
- auto pending = eldbus_proxy_send(connectionState.proxy.get(), msg.release(), callAsyncCb, cbData, ELDBUS_CALL_TIMEOUT);
- if (pending)
- eldbus_pending_free_cb_add(pending, pendingFreeCb, cbData);
- else {
- DEBUG("call %d: sending failed", callId.id);
- delete cbData;
- callback({});
- return;
}
- DEBUG("call %d: call sent", callId.id);
- }
- static void listenerCallback(void *data, const Eldbus_Message *msg)
- {
- (*((std::function<void(const Eldbus_Message *msg)> *)data))(msg);
- }
- template <typename ... ARGS> void addListener(CallId callId, ConnectionState &connectionState,
- const std::string &func_name,
- std::vector<std::function<void(const Eldbus_Message *msg)>*> &callbacks,
- std::function<void(ARGS...)> callback)
- {
- auto tmp = new std::function<void(const Eldbus_Message *msg)> {
- [ callId, connectionState, callback ](const Eldbus_Message * msg) -> void {
- const char *errname, *aux;
- if (eldbus_message_error_get(msg, &errname, &aux))
+ template <typename ... ARGS> SignalId addSignal(const std::string &memberName)
+ {
+ detail::CallId callId;
+ signals.push_back({});
+ auto &z = signals.back();
+ z.memberName = memberName;
+ z.args = detail::EldbusArgGenerator_Args<void(ARGS...)>::get(strings);
+ z.id = callId;
+ DBUS_DEBUG("call %d: signal %s", callId.id, memberName.c_str());
+ return SignalId { callId };
+ }
+
+ private:
+ std::vector<MethodInfo> methods;
+ std::vector<PropertyInfo> properties;
+ std::vector<SignalInfo> signals;
+ std::string interfaceName;
+ detail::StringStorage strings;
+
+ template <typename T> std::function<Eldbus_Message*(const Eldbus_Message *msg)> construct(detail::CallId callId,
+ typename detail::dbus_interface_traits<T>::SyncCB callback)
+ {
+ using VEArgs = typename detail::dbus_interface_traits<T>::VEArgs;
+ return [ = ](const Eldbus_Message * msg) -> Eldbus_Message* {
+ Eldbus_Message *ret = nullptr;
+ auto args = detail::unpackValues<VEArgs>(callId, msg);
+ if (args)
+ {
+ auto v = detail::apply(callback, std::move(args.getValues()));
+ if (v) {
+ DBUS_DEBUG("call %d: success", callId.id);
+ ret = eldbus_message_method_return_new(msg);
+ packValues(callId, ret, v);
+ } else {
+ DBUS_DEBUG("call %d: failed: %s", callId.id, v.getError().message.c_str());
+ ret = eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.Failed", v.getError().message.c_str());
+ }
+ } else
{
- ERROR("call %d: Eldbus error: %s %s", callId.id, errname, aux);
- return;
+ std::ostringstream err;
+ err << "expected signature '" << detail::signature<VEArgs>::sig() <<
+ "', got '" << eldbus_message_signature_get(msg) << "'";
+ auto str = err.str();
+ DBUS_DEBUG("call %d: failed: %s", callId.id, str.c_str());
+ ret = eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.InvalidArgs", str.c_str());
}
+ return ret;
+ };
+ }
+ };
- DEBUG("call %d: received signal with signature '%s'", callId.id, eldbus_message_signature_get(msg));
- std::tuple<ARGS...> params;
- if (detail::unpackValues(callId, params, msg))
- detail::apply<std::function<void(ARGS...)>, void, ARGS...>(callback, params);
- }
- };
- eldbus_proxy_signal_handler_add(connectionState.proxy.get(), func_name.c_str(), listenerCallback, std::move(tmp));
- callbacks.push_back(tmp);
- }
- template <typename ... ARGS>
- void displayDebugCallInfo(CallId callId, const std::string &func_name, const std::string &info, const ARGS &...)
- {
- DEBUG("call %d: %s fname = %s", callId, info.c_str(), func_name.c_str());
- }
- template <typename ... ARGS>
- void displayDebugCallInfo(CallId callId, const std::string &func_name, std::string info, std::string a, const std::string &b, const ARGS &...)
+ class DBusServer : private detail::EldbusProxyBase
{
- if (func_name == "Get" && info.find(" iname = ") == std::string::npos)
- DEBUG("call %d: %s iname = %s pname = %s", callId, info.c_str(), a.c_str(), b.c_str());
- else
- displayDebugCallInfo(callId, func_name, info);
- }
-}
-
-class DBus : private detail::EldbusProxyBase
-{
-public:
- enum class ConnectionType {
- SYSTEM, SESSION
- };
- DBus();
- DBus(std::string bus_name, std::string path_name, std::string interface_name,
- ConnectionType tp);
- DBus(std::string bus_name, std::string path_name, std::string interface_name,
- const EldbusConnectionCallbackHandle &conn = {});
- ~DBus();
+ public:
+ DBusServer();
+ DBusServer(ConnectionType tp);
+ DBusServer(const EldbusConnectionCallbackHandle &conn);
+ ~DBusServer();
- DBus(const DBus &) = default;
- DBus(DBus &&) = default;
+ DBusServer(const DBusServer &) = delete;
+ DBusServer(DBusServer &&) = default;
- DBus &operator = (DBus &&) = default;
- DBus &operator = (const DBus &) = default;
+ DBusServer &operator = (DBusServer &&) = default;
+ DBusServer &operator = (const DBusServer &) = delete;
- explicit operator bool () const
- {
- return bool(connectionState.proxy);
- }
+ void addInterface(const std::string &pathName, DBusInterfaceDescription &dscr, bool fallback = false);
+ std::string getBusName() const;
- template <typename Q> using Method = detail::Method<Q>;
- template <typename Q> using Listen = detail::Listen<Q>;
+ template <typename ... ARGS> void emit(DBusInterfaceDescription::SignalId signal, const ARGS &... args)
+ {
+ auto it = signalData.find(signal.id.id);
+ if (it != signalData.end()) {
+ auto msg = eldbus_service_signal_new(it->second.first, it->second.second);
+ detail::packValues(signal.id, msg, args...);
+ eldbus_service_signal_send(it->second.first, msg);
+ } else {
+ DBUS_DEBUG("signal %d not found", signal.id.id);
+ }
+ }
+ static std::string getCurrentObjectPath();
+ static EldbusConnectionCallbackHandle getCurrentConnection();
+ private:
+ EldbusConnectionCallbackHandle connection;
+ std::vector<std::function<void()>> destructors;
+ std::unordered_map<unsigned int, std::pair<const Eldbus_Service_Interface *, unsigned int>> signalData;
+ };
- template <typename R>
- Method<R> method(std::string func_name)
+ template <typename T> void DBusInterfaceDescription::addAsyncMethod(const std::string &memberName, typename detail::dbus_interface_traits<T>::AsyncCB callback)
{
- return Method<R> { connectionState, std::move(func_name), info };
- }
+ detail::CallId callId;
+ MethodInfo mi;
+ methods.push_back(std::move(mi));
+ auto &z = methods.back();
+ z.in = detail::EldbusArgGenerator_Args<T>::get(strings);
+ z.out = detail::EldbusArgGenerator_ReturnType<T>::get(strings);
+ z.memberName = memberName;
+ DBUS_DEBUG("call %d: method %s, in %s, out %s", callId.id, memberName.c_str(),
+ detail::EldbusArgGenerator_Args<T>::name().c_str(),
+ detail::EldbusArgGenerator_ReturnType<T>::name().c_str()
+ );
+ using VEArgs = typename detail::dbus_interface_traits<T>::VEArgs;
+ z.callback = [ = ](const Eldbus_Message * msg) -> Eldbus_Message* {
+ struct CallState {
+ bool replyRunning = true;
+ Eldbus_Message *reply = nullptr;
+ EldbusMessageCallbackHandle message;
+ };
+ auto callState = std::make_shared<CallState>();
+ callState->message.reset(eldbus_message_ref(const_cast<Eldbus_Message *>(msg)));
+ auto connection = DBusServer::getCurrentConnection();
+ auto retCallback = [ = ](typename detail::dbus_interface_traits<T>::Ret v)
+ {
+ DBUS_DEBUG(".");
+ if (v) {
+ DBUS_DEBUG(".");
+ callState->reply = eldbus_message_method_return_new(callState->message.get());
+ packValues(callId, callState->reply, v);
+ } else {
+ DBUS_DEBUG("call %d: failed: %s", callId.id, v.getError().message.c_str());
+ callState->reply = eldbus_message_error_new(callState->message.get(), "org.freedesktop.DBus.Error.Failed", v.getError().message.c_str());
+ }
+ if (!callState->replyRunning) {
+ DBUS_DEBUG(".");
+ eldbus_connection_send(connection.get(), callState->reply, NULL, NULL, -1);
+ }
+ };
+ Eldbus_Message *ret = nullptr;
+ auto args = detail::unpackValues<VEArgs>(callId, msg);
+ if (args)
+ {
+ detail::apply(callback, std::move(retCallback), std::move(args.getValues()));
+ callState->replyRunning = false;
+ ret = callState->reply;
+ } else
+ {
+ std::ostringstream err;
+ err << "expected signature '" << detail::signature<VEArgs>::sig() <<
+ "', got '" << eldbus_message_signature_get(msg) << "'";
+ auto str = err.str();
+ ret = eldbus_message_error_new(msg, "org.freedesktop.DBus.Error.InvalidArgs", str.c_str());
+ }
+ return ret;
+ };
- template <typename R>
- Listen<R> listen(std::string func_name)
- {
- return Listen<R> { connectionState, callbacks, std::move(func_name), info };
+ z.id = callId;
}
-private:
- detail::ConnectionState connectionState;
- std::vector<std::function<void(const Eldbus_Message *)>*> callbacks;
- std::string info;
-};
-
-template <typename RetType, typename ... ARGS> struct detail::Method<RetType(ARGS...)> {
- ConnectionState connectionState;
- std::string func_name;
- std::string info;
+ EldbusConnectionCallbackHandle getDBusConnectionByType(ConnectionType tp);
+ EldbusConnectionCallbackHandle getDBusConnectionByName(const std::string &name);
+ std::string getConnectionName(const EldbusConnectionCallbackHandle &);
+}
- Optional<RetType> call(const ARGS &... args)
+namespace std
+{
+ template <size_t INDEX, typename ... ARGS> inline auto &get(DBus::ValueOrError<ARGS...> &v)
{
- CallId callId;
- detail::displayDebugCallInfo(callId, func_name, info, args...);
- auto reply = detail::call(callId, connectionState, func_name, args...);
- if (reply) {
- RetType r;
- if (detail::unpackValues<RetType>(callId, r, reply.get()))
- return r;
- }
- return {};
+ return std::get<INDEX>(v.getValues());
}
- void asyncCall(const ARGS &... args, std::function<void(Optional<RetType>)> callback)
+ template <size_t INDEX, typename ... ARGS> inline auto get(const DBus::ValueOrError<ARGS...> &v)
{
- CallId callId;
- detail::displayDebugCallInfo(callId, func_name, info, args...);
- auto connectionState = this->connectionState;
- auto fn = func_name;
- auto cc = [callId, connectionState, callback](const Eldbus_Message * reply) {
- DEBUG("call %d: processing asynchronous reply (%d)", callId.id, reply ? 1 : 0);
- if (!reply)
- callback({});
- else {
- RetType r;
- if (!detail::unpackValues<RetType>(callId, r, reply))
- callback({});
- else
- callback(r);
- }
- DEBUG("call %d: processing asynchronous reply done", callId.id);
- };
- detail::asyncCall(callId, connectionState, func_name, std::move(cc), args...);
+ return std::get<INDEX>(v.getValues());
}
-};
-template <typename ... ARGS> struct detail::Method<void(ARGS...)> {
- ConnectionState connectionState;
- std::string func_name;
- std::string info;
+}
- bool call(const ARGS &... args)
- {
- CallId callId;
- detail::displayDebugCallInfo(callId, func_name, info, args...);
- auto reply = detail::call(callId, connectionState, func_name, args...);
- return bool(reply);
- }
- void asyncCall(const ARGS &... args, std::function<void(bool)> callback)
- {
- CallId callId;
- detail::displayDebugCallInfo(callId, func_name, info, args...);
- auto connectionState = this->connectionState;
- auto fn = func_name;
- auto cc = [callId, connectionState, callback](const Eldbus_Message * reply) {
- DEBUG("call %d: processing asynchronous reply", callId.id);
- if (!reply)
- callback(false);
- else {
- callback(true);
- }
- DEBUG("call %d: processing asynchronous reply done", callId.id);
- };
- detail::asyncCall(callId, connectionState, func_name, std::move(cc), args...);
- }
-};
-template <typename ... ARGS> struct detail::Listen<void(ARGS...)> {
- detail::ConnectionState connectionState;
- std::vector<std::function<void(const Eldbus_Message *)>*> &callbacks;
- std::string func_name;
- std::string info;
-
- void add(std::function<void(ARGS...)> cc)
- {
- CallId callId;
- detail::displayDebugCallInfo(callId, func_name, info);
- detail::addListener<ARGS...>(callId, connectionState, func_name, callbacks, std::move(cc));
- }
-};
+#undef DBUS_DEBUG
#endif
--- /dev/null
+#include "DBus.hpp"
+#include <gtest/gtest.h>
+#include <atspi/atspi.h>
+#include <glib.h>
+#include <iostream>
+#include <Ecore.h>
+#include <Ecore_Input.h>
+#include <Elementary.h>
+#include <tizen.h>
+#include <app.h>
+#include <thread>
+#include <functional>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+struct TimerData {
+ std::function<void()> callback;
+ Ecore_Timer *timer = nullptr;
+};
+static std::vector<std::unique_ptr<TimerData>> callbacks;
+
+class DBusTest : public ::testing::Test
+{
+protected:
+ bool success = false, done = false;
+
+ static Eina_Bool callback_fnc(void *data)
+ {
+ auto index = reinterpret_cast<unsigned int>(data);
+ callbacks[index]->callback();
+ callbacks[index]->timer = nullptr;
+ return ECORE_CALLBACK_CANCEL;
+ }
+ void SetUp(void) override
+ {
+ static bool init = true;
+ if (init) {
+ init = false;
+ auto z = ecore_init();
+ ASSERT_EQ(z, 1);
+ ecore_event_init();
+ eldbus_init();
+ // DBus::setDebugPrinter([](const char *txt, size_t s) {
+ // std::cerr << txt << std::endl;
+ // });
+ }
+ }
+ void cancelAll()
+ {
+ for (auto &a : callbacks) {
+ if (a->timer)
+ ecore_timer_del(a->timer);
+ }
+ callbacks.resize(0);
+ }
+ void TearDown() override
+ {
+ cancelAll();
+ }
+ void finish(bool succ)
+ {
+ cancelAll();
+ success = succ;
+ ecore_main_loop_quit();
+ }
+ void addTimer(float timeout, std::function<void()> callback)
+ {
+ auto t = std::make_unique<TimerData>();
+ t->callback = std::move(callback);
+ auto index = (unsigned int)callbacks.size();
+ callbacks.push_back(std::move(t));
+ callbacks.back()->timer = ecore_timer_add(timeout, callback_fnc, reinterpret_cast<void *>(index));
+ }
+ void runTestInMainLoop(float timeout)
+ {
+ success = false;
+ done = false;
+ addTimer(timeout, [this]() {
+ done = true;
+ ecore_main_loop_quit();
+ return ECORE_CALLBACK_CANCEL;
+ });
+ ecore_main_loop_begin();
+ }
+
+ static void addValues(std::map<int, int> &dst, std::map<int, int> &s1, std::map<int, int> &s2)
+ {
+ for (auto &a : s1)
+ dst[a.first] = a.second + s2[a.first];
+ }
+ static void addValues(std::unordered_map<int, int> &dst, std::unordered_map<int, int> &s1, std::unordered_map<int, int> &s2)
+ {
+ for (auto &a : s1)
+ dst[a.first] = a.second + s2[a.first];
+ }
+ static void addValues(std::vector<int> &dst, std::vector<int> &s1, std::vector<int> &s2)
+ {
+ dst.resize(s1.size());
+ for (size_t i = 0; i < s1.size(); ++i) {
+ dst[i] = s1[i] + s2[i];
+ }
+ }
+ template <size_t I> static void addValues(std::array<int, I> &dst, std::array<int, I> &s1, std::array<int, I> &s2)
+ {
+ for (size_t i = 0; i < I; ++i) {
+ dst[i] = s1[i] + s2[i];
+ }
+ }
+ template <typename T1, typename T2> static void compareValues(T1 src, T2 expected)
+ {
+ EXPECT_EQ(src.size(), expected.size());
+ if (src.size() == expected.size()) {
+ using T = std::reference_wrapper<typename T1::value_type>;
+ std::vector<T> s { src.begin(), src.end() }, e { expected.begin(), expected.end() };
+ std::sort(s.begin(), s.end(), [](auto a, auto b) {
+ return a.get() < b.get();
+ });
+ std::sort(e.begin(), e.end(), [](auto a, auto b) {
+ return a.get() < b.get();
+ });
+ for (size_t i = 0; i < s.size(); ++i) {
+ EXPECT_EQ(s[i].get(), e[i].get());
+ }
+ }
+ }
+
+ template <typename S, typename T = S> void sendAndReceiveT(S source, T expected)
+ {
+ auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+ DBus::DBusInterfaceDescription dsc("itest.itest");
+ dsc.addMethod<void(T)>("ftest", [ = ](T a) -> DBus::ValueOrError<void> {
+ EXPECT_EQ(a.size(), expected.size());
+ if (a.size() == expected.size())
+ {
+ compareValues(a, expected);
+ }
+ return {};
+ });
+
+ DBus::DBusServer server{connection};
+ server.addInterface("/ptest", dsc);
+
+ DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+ db.method<void(S)>("ftest").asyncCall([ = ](DBus::ValueOrError<void> val) {
+ EXPECT_TRUE(val);
+ finish(true);
+ }, source);
+ runTestInMainLoop(5.0f);
+ EXPECT_TRUE(success);
+ }
+ template <typename S, typename R> void sendAndReceiveMultipleArgs()
+ {
+ auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+ DBus::DBusInterfaceDescription dsc("itest.itest");
+ dsc.addMethod<R(int, int)>("ftest", [](int a, int b) -> DBus::ValueOrError<R> {
+ return { { a, b} };
+ });
+
+ DBus::DBusServer server{connection};
+ server.addInterface("/ptest", dsc);
+
+ DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+ db.method<S(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<S> val) {
+ EXPECT_TRUE(val);
+ if (val) {
+ auto v = std::get<0>(val);
+ EXPECT_EQ(std::get<0>(v), 7);
+ EXPECT_EQ(std::get<1>(v), 15);
+ }
+ finish(bool(val));
+ }, 7, 15);
+ runTestInMainLoop(5.0f);
+ EXPECT_TRUE(success);
+ }
+};
+
+TEST_F(DBusTest, timeouted)
+{
+ runTestInMainLoop(0.1f);
+ EXPECT_FALSE(success);
+}
+
+// calls asynchronously a method. checks, error is passed correctly.
+TEST_F(DBusTest, sendAndReceiveError)
+{
+ auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+ DBus::DBusInterfaceDescription dsc("itest.itest");
+ dsc.addMethod<int(int, int)>("ftest", [](int a, int b) -> DBus::ValueOrError<int> {
+ return DBus::Error{ "error"};
+ });
+
+ DBus::DBusServer server{connection};
+ server.addInterface("/ptest", dsc);
+
+ DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+ db.method<int(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<int> val) {
+ EXPECT_FALSE(val);
+ finish(true);
+ }, 7, 15);
+ runTestInMainLoop(5.0f);
+ EXPECT_TRUE(success);
+}
+
+// calls asynchronously a method. checks, int marshalls property.
+TEST_F(DBusTest, sendAndReceiveInt)
+{
+ auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+ DBus::DBusInterfaceDescription dsc("itest.itest");
+ dsc.addMethod<int(int, int)>("ftest", [](int a, int b) -> DBus::ValueOrError<int> {
+ return a + b;
+ });
+
+ DBus::DBusServer server{connection};
+ server.addInterface("/ptest", dsc);
+
+ DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+ db.method<int(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<int> val) {
+ EXPECT_TRUE(val);
+ if (val)
+ EXPECT_EQ(std::get<0>(val), 7 + 15);
+ finish(true);
+ }, 7, 15);
+ runTestInMainLoop(5.0f);
+ EXPECT_TRUE(success);
+}
+
+// calls method on extern process asynchronously, which in turn calls
+// method asynchronously on the original process. Checks, if asynchronous return value production
+// works - responding callback receives reply callback, which should be called, when return data is ready
+TEST_F(DBusTest, sendAndReceiveAsyncInt)
+{
+ auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+ DBus::DBusInterfaceDescription dsc("itest.itest");
+ dsc.addAsyncMethod<int(int, int)>("ftest", [&](std::function<void(DBus::ValueOrError<int>)> reply, int a, int b) {
+ if (b == 0) {
+ reply(a + 10);
+ } else if (b == 1) {
+ reply(DBus::Error{"error1"});
+ } else if (b == 2) {
+ addTimer(0.0f, [ = ]() {
+ reply(a + 15);
+ });
+ } else if (b == 3) {
+ addTimer(0.0f, [ = ]() {
+ reply(DBus::Error { "error2"});
+ });
+ } else {
+ EXPECT_TRUE(0);
+ }
+ });
+
+ DBus::DBusServer server{connection};
+ server.addInterface("/ptest", dsc);
+
+ DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+ db.method<int(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<int> val) {
+ EXPECT_TRUE(val);
+ if (val) {
+ EXPECT_EQ(std::get<0>(val), 7 + 10);
+ finish(true);
+ } else {
+ finish(false);
+ }
+ }, 7, 0);
+ runTestInMainLoop(1.0f);
+ ASSERT_TRUE(success);
+
+ db.method<int(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<int> val) {
+ EXPECT_FALSE(val);
+ if (!val) {
+ EXPECT_EQ(val.getError().message, "org.freedesktop.DBus.Error.Failed: error1");
+ finish(true);
+ } else {
+ finish(false);
+ }
+ }, 17, 1);
+ runTestInMainLoop(1.0f);
+ ASSERT_TRUE(success);
+
+ db.method<int(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<int> val) {
+ EXPECT_TRUE(val);
+ if (val) {
+ EXPECT_EQ(std::get<0>(val), 27 + 15);
+ finish(true);
+ } else {
+ finish(false);
+ }
+ }, 27, 2);
+ runTestInMainLoop(1.0f);
+ ASSERT_TRUE(success);
+
+ db.method<int(int, int)>("ftest").asyncCall([ = ](DBus::ValueOrError<int> val) {
+ EXPECT_FALSE(val);
+ if (!val) {
+ EXPECT_EQ(val.getError().message, "org.freedesktop.DBus.Error.Failed: error2");
+ finish(true);
+ } else {
+ finish(false);
+ }
+ }, 37, 3);
+ runTestInMainLoop(1.0f);
+ ASSERT_TRUE(success);
+}
+
+// calls asynchronously a method. checks, std::vector marshalls property.
+TEST_F(DBusTest, sendAndReceiveVector)
+{
+ sendAndReceiveT<std::vector<int>>({ 1, 2 }, { 1, 2 });
+}
+
+// calls asynchronously a method. checks, std::vector can be received as std::array
+TEST_F(DBusTest, sendAndReceiveVector2)
+{
+ std::array<int, 2> arr { 1, 2 };
+ sendAndReceiveT<std::vector<int>, std::array<int, 2>>({ 1, 2 }, arr);
+}
+
+// calls asynchronously a method. checks, std::array marshalls property.
+TEST_F(DBusTest, sendAndReceiveArray)
+{
+ std::array<int, 2> arr { 1, 2 };
+ sendAndReceiveT<std::array<int, 2>>(arr, arr);
+}
+
+// calls asynchronously a method. checks, std::array can be received as std::vector
+TEST_F(DBusTest, sendAndReceiveArray2)
+{
+ std::array<int, 2> arr { 1, 2 };
+ sendAndReceiveT<std::array<int, 2>, std::vector<int>>(arr, { 1, 2 });
+}
+
+// calls asynchronously a method. checks, std::map marshalls property.
+TEST_F(DBusTest, sendAndReceiveMap)
+{
+ sendAndReceiveT<std::map<int, int>>({ { 1, 1}, {2, 2} }, { { 1, 1}, {2, 2} });
+}
+
+// calls asynchronously a method. checks, std::unordered_map marshalls property.
+TEST_F(DBusTest, sendAndReceiveUnorderedMap)
+{
+ sendAndReceiveT<std::unordered_map<int, int>>({ { 1, 1}, {2, 2} }, { { 1, 1}, {2, 2} });
+}
+
+// calls asynchronously a method. checks, if std::tuple of values can be
+// received as std::pair
+TEST_F(DBusTest, sendAndReceiveMultipleArgs)
+{
+ sendAndReceiveMultipleArgs<std::tuple<int, int>, std::pair<int, int>>();
+}
+
+// calls asynchronously a method. checks, std::tuple marshalls property.
+TEST_F(DBusTest, sendAndReceiveMultipleArgs2)
+{
+ sendAndReceiveMultipleArgs<std::tuple<int, int>, std::tuple<int, int>>();
+}
+
+// calls asynchronously a method. checks, std::pair marshalls property.
+TEST_F(DBusTest, sendAndReceiveMultipleArgs3)
+{
+ sendAndReceiveMultipleArgs<std::pair<int, int>, std::pair<int, int>>();
+}
+
+// calls asynchronously a method. checks, if std::pair of values can be
+// received as std::tuple
+TEST_F(DBusTest, sendAndReceiveMultipleArgs4)
+{
+ sendAndReceiveMultipleArgs<std::pair<int, int>, std::tuple<int, int>>();
+}
+
+// gets property asynchronously, then sets property asynchronously,
+// then gets it again to check, if it has correct value
+TEST_F(DBusTest, sendAndReceiveProperty)
+{
+ auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+ DBus::DBusInterfaceDescription dsc("itest.itest");
+ int realVal = 10;
+ dsc.addProperty<int>("ftest",
+ [&]() -> DBus::ValueOrError<int> {
+ return realVal;
+ },
+ [&](int q) -> DBus::ValueOrError<void> {
+ realVal = q;
+ return DBus::Success{};
+ });
+
+ DBus::DBusServer server{connection};
+ server.addInterface("/ptest", dsc);
+
+ DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+ db.property<int>("ftest").asyncGet([ & ](DBus::ValueOrError<int> val) {
+ EXPECT_TRUE(val);
+ if (val) {
+ EXPECT_EQ(std::get<0>(val), 10);
+ EXPECT_EQ(realVal, 10);
+
+ db.property<int>("ftest").asyncSet([ & ](DBus::ValueOrError<void> val) {
+ EXPECT_TRUE(val);
+ EXPECT_EQ(realVal, 15);
+ if (val) {
+ db.property<int>("ftest").asyncGet([ & ](DBus::ValueOrError<int> val) {
+ EXPECT_TRUE(val);
+ if (val) {
+ EXPECT_EQ(std::get<0>(val), 15);
+ EXPECT_EQ(realVal, 15);
+ finish(true);
+ } else {
+ finish(false);
+ }
+ });
+ } else {
+ finish(false);
+ }
+ }, 15);
+ } else {
+ finish(false);
+ }
+ });
+ runTestInMainLoop(1.0f);
+ EXPECT_TRUE(success);
+}
+
+// gets property asynchronously, then sets property asynchronously,
+// then gets it again to check, if it has correct value
+// uses fallback interface (request path must begins with property interface path)
+TEST_F(DBusTest, sendAndReceiveFallbackProperty)
+{
+ auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+ DBus::DBusInterfaceDescription dsc("itest.itest");
+ std::unordered_map<std::string, int> values;
+
+ dsc.addProperty<int>("ftest",
+ [&]() -> DBus::ValueOrError<int> {
+ auto it = values.find(DBus::DBusServer::getCurrentObjectPath());
+ EXPECT_TRUE(it != values.end());
+ if (it != values.end()) return it->second;
+ return -1;
+ },
+ [&](int q) -> DBus::ValueOrError<void> {
+ values[DBus::DBusServer::getCurrentObjectPath()] = q;
+ return DBus::Success{};
+ });
+
+ DBus::DBusServer server{connection};
+ server.addInterface("/ptest", dsc, true);
+ unsigned int doneCount = 0;
+ std::vector<std::string> paths { "/ptest", "/ptest/p1", "/ptest/p2" };
+ for (auto p : paths)
+ values[p] = 10 + (int)p.size();
+
+ for (auto path : paths) {
+ DBus::DBusClient db{server.getBusName(), path, "itest.itest", connection};
+ db.property<int>("ftest").asyncGet([ & ](DBus::ValueOrError<int> val) {
+ EXPECT_TRUE(val);
+ if (val) {
+ EXPECT_EQ(std::get<0>(val), 10 + (int)path.size());
+ EXPECT_EQ(values[path], 10 + (int)path.size());
+
+ db.property<int>("ftest").asyncSet([ & ](DBus::ValueOrError<void> val) {
+ EXPECT_TRUE(val);
+ EXPECT_EQ(values[path], 30 + (int)path.size());
+ if (val) {
+ db.property<int>("ftest").asyncGet([ & ](DBus::ValueOrError<int> val) {
+ EXPECT_TRUE(val);
+ ++doneCount;
+ if (val) {
+ EXPECT_EQ(std::get<0>(val), 30 + (int)path.size());
+ EXPECT_EQ(values[path], 30 + (int)path.size());
+ finish(true);
+ } else {
+ finish(false);
+ }
+ });
+ } else {
+ finish(false);
+ }
+ }, 30 + path.size());
+ } else {
+ finish(false);
+ }
+ });
+ runTestInMainLoop(1.0f);
+ ASSERT_TRUE(success);
+ }
+}
+
+// emits and catches signal
+TEST_F(DBusTest, sendAndReceiveSignal)
+{
+ auto connection = getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+ DBus::DBusInterfaceDescription dsc("itest.itest");
+ std::unordered_map<std::string, int> values;
+
+ auto signalId = dsc.addSignal<int, int>("ftest");
+ DBus::DBusServer server{connection};
+ server.addInterface("/ptest", dsc, false);
+
+ DBus::DBusClient db{server.getBusName(), "/ptest", "itest.itest", connection};
+ int signalValue = 1;
+ db.addSignal<void(int, int)>("ftest", [&](int a1, int a2) {
+ signalValue = a1 + a2;
+ finish(true);
+ });
+ addTimer(0.0f, [&]() {
+ server.emit<int, int>(signalId, 11, 356);
+ });
+ runTestInMainLoop(1.0f);
+ EXPECT_TRUE(success);
+ EXPECT_EQ(signalValue, 11 + 356);
+}
+
+static const std::string syncInterfaceName = "sitest.sitest";
+static const std::string syncPathName = "/stest";
+static std::string testBusName;
+
+// Calls method on external process asynchronously
+TEST_F(DBusTest, sendAndReceiveMethodAsync)
+{
+ auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+ DBus::DBusClient db{testBusName, syncPathName, syncInterfaceName, connection};
+ db.method<int(int, int)>("add").asyncCall([ = ](DBus::ValueOrError<int> val) {
+ EXPECT_TRUE(val);
+ if (val) {
+ EXPECT_EQ(std::get<0>(val), 10 + 55);
+ finish(true);
+ } else {
+ finish(false);
+ }
+ }, 10, 55);
+ runTestInMainLoop(1.0f);
+ EXPECT_TRUE(success);
+}
+
+// Calls method on external process synchronously
+TEST_F(DBusTest, synchronousCall)
+{
+ auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+ DBus::DBusClient db{testBusName, syncPathName, syncInterfaceName, connection};
+ auto r = db.method<int(int, int)>("add").call(10, 55);
+ ASSERT_TRUE(r);
+ ASSERT_EQ(std::get<0>(r), 10 + 55);
+ r = db.method<int(int, int)>("add").call(-10, 55);
+ ASSERT_FALSE(r);
+ ASSERT_EQ(r.getError().message, "org.freedesktop.DBus.Error.Failed: negative");
+}
+
+// Calls method 'call' on external process asynchronously,
+// which calls back synchronously method 'setReply' on original process
+TEST_F(DBusTest, recursiveCall)
+{
+ auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+
+ DBus::DBusInterfaceDescription dsc("itest.itest2");
+ int calledValue = 1;
+ dsc.addMethod<int(int)>("setReply", [&](int val) -> DBus::ValueOrError<int> {
+ calledValue = val;
+ return val + 10;
+ });
+
+ DBus::DBusServer server{connection};
+ server.addInterface("/ptesttest", dsc, false);
+
+ DBus::DBusClient db{testBusName, syncPathName, syncInterfaceName, connection};
+ db.method<int(int, int)>("call").asyncCall([&](DBus::ValueOrError<int> val) {
+ EXPECT_TRUE(val);
+ if (val)
+ EXPECT_EQ(std::get<0>(val), 111 + 10);
+ finish(bool(val));
+ }, server.getBusName(), "/ptesttest", "itest.itest2", "setReply", 111);
+
+ runTestInMainLoop(1.0f);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(calledValue, 111);
+}
+
+// Calls method 'emitS' on external process asynchronously,
+// which emits signal, which is received on original process
+TEST_F(DBusTest, recursiveSignal)
+{
+ auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+
+ DBus::DBusClient db{testBusName, syncPathName, syncInterfaceName, connection};
+ int realValue = 1;
+ db.addSignal<void(int)>("signal", [&](int val) {
+ EXPECT_EQ(val, 20);
+ realValue = val;
+ finish(true);
+ });
+
+ db.method<int(int)>("emitS").asyncCall([&](DBus::ValueOrError<int> val) {
+ EXPECT_TRUE(val);
+ if (val)
+ EXPECT_EQ(std::get<0>(val), 10 + 30);
+ finish(bool(val));
+ }, 10);
+
+ runTestInMainLoop(1.0f);
+ ASSERT_TRUE(success);
+ ASSERT_EQ(realValue, 20);
+}
+
+std::pair<std::vector<std::unique_ptr<DBus::DBusClient>>, std::vector<std::unique_ptr<DBus::DBusServer>>>
+ initializeServices(char *busNameDest, size_t busNameSize)
+{
+ auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+ std::string busName = eldbus_connection_unique_name_get(connection.get());
+ assert(busName.size() + 2 < busNameSize);
+ strncpy(busNameDest + 1, busName.c_str(), busName.size());
+ busNameDest[0] = 1;
+
+ std::vector<std::unique_ptr<DBus::DBusClient>> clients;
+ std::vector<std::unique_ptr<DBus::DBusServer>> servers;
+
+ auto server = [&]() -> DBus::DBusServer * {
+ auto s = std::make_unique<DBus::DBusServer>(connection);
+ servers.push_back(std::move(s));
+ return servers.back().get();
+ };
+
+ auto s = server();
+ DBus::DBusInterfaceDescription descr(syncInterfaceName);
+
+ auto signal = descr.addSignal<int>("signal");
+ descr.addMethod<int(int, int)>("add", [ = ](int a, int b) -> DBus::ValueOrError<int> {
+ if (a >= 0 && b >= 0)
+ {
+ return a + b;
+ }
+ return DBus::Error { "negative" };
+ });
+ descr.addMethod<int(int)>("emitS", [ = ](int a) -> DBus::ValueOrError<int> {
+ s->emit<int>(signal, a + 10);
+ return a + 20;
+ });
+ descr.addMethod<int(std::string, std::string, std::string, std::string, int)>("call", [ = ](std::string busName, std::string pName, std::string iName, std::string methodName, int a) -> DBus::ValueOrError<int> {
+ auto connection = DBus::getDBusConnectionByType(DBus::ConnectionType::SYSTEM);
+ DBus::DBusClient client { busName, std::move(pName), std::move(iName), connection };
+ return client.method<int(int)>(methodName).call(a);
+ });
+ s->addInterface(syncPathName, descr, false);
+ return { std::move(clients), std::move(servers) };
+}
+
+void runServer(char *busNameDest, size_t busNameSize)
+{
+ // DBus::setDebugPrinter([](const char *txt, size_t s) {
+ // std::cerr << txt << std::endl;
+ // });
+ ecore_init();
+ ecore_event_init();
+ eldbus_init();
+ auto s = initializeServices(busNameDest, busNameSize);
+ ecore_main_loop_begin();
+}
+
+int main(int argc, char *argv[])
+{
+ constexpr unsigned int size = 4096;
+ char *sharedMem = (char *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
+ if (!sharedMem) return 1;
+ memset(sharedMem, 0, size);
+
+ auto c = fork();
+ if (c == 0) {
+ // child
+ runServer(sharedMem, size);
+ } else if (c > 0) {
+ // parent
+ DEBUG("main function called");
+ auto now = std::chrono::high_resolution_clock::now() + std::chrono::seconds(1);
+
+ while (now > std::chrono::high_resolution_clock::now()) {
+ if (((volatile char *)sharedMem)[0]) break;
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
+ if (!((volatile char *)sharedMem)[0]) return 1;
+ testBusName = sharedMem + 1;
+
+ int r = 1;
+ try {
+ ::testing::InitGoogleTest(&argc, argv);
+ r = RUN_ALL_TESTS();
+ } catch (...) {
+ DEBUG("exception");
+ r = 1;
+ }
+ kill(c, 9);
+ return r;
+ } else {
+ ASSERT(0, "failed to fork");
+ return 1;
+ }
+ return 0;
+}