/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* C++ wrapper for libatspi, so to make it less obnoxious to use */
/** * Adding a new wrapper * * To wrap a new Atspi type (let's say, AtspiCollection), you need to: * * 1. Add <tt>DEFINE_GOBJECT_CAST(AtspiCollection, ATSPI_TYPE_COLLECTION)</tt> near the similar * ones. This creates <tt>Atspi::cast<AtspiCollection*>(p)</tt> so that such a cast based on the * C++ type is checked using the GType type system. * 2. Add a declaration for the new wrapper class above Atspi::Accessible * (<tt>class Collection;</tt>) so it can be used in step 3. * 3. Add <tt>Atspi::Accessible::queryCollection()</tt> method. Its definition has to be in the * source file as it requires a complete type for the wrapper class. The body just calls * <tt>queryInterface<Collection>(atspi_accessible_get_collection_iface);</tt> and returns * its value. * 4. Add the definition of the new wrapper class: * <tt>class Collection : public Accessible { ... }</tt> * Use the existing wrappers as inspiration, but basically: * - Define the constructor that only chains up to the parent * - Define each wrapper method, which generally only have to call one of the <tt>invoke()</tt> * helpers to wrap the C calls. There are a few, depending on some details of the C call: * - @c GObjectWrapperBase::invoke(): this is the most basic one, that just calls the C method * on @c GObjectWrapperBase::get() with the given arguments. Use this for calls not * throwing an exception and either returning a plain value, or something not handled by * one of the others below. * - @c AtspiWrapperBase::invokeThrow(): like @c GObjectWrapperBase::invoke(), but for C calls * that take a @c GError argument for throwing exceptions. @c invokeThrow() will * transform any C exception into a a C++ exception (@c css::uno::RuntimeException) * - @c AtspiWrapperBase::strInvoke(): like @c AtspiWrapperBase::invokeThrow(), but manages a * C string (@c char*) return value as an @c std::string. Use this for C calls returning * a C string. * - @c AtspiWrapperBase::garrayInvoke(): like @c AtspiWrapperBase::invokeThrow(), but manages * a @c GArray return value as an @c std::vector. Use this for C calls returning a * @p GArray. * - @c AtspiWrapperBase::hashMapInvoke(): like @c AtspiWrapperBase::invokeThrow(), but manages * a @c GHashTable return value as an @c std::unordered_map. Use this for C calls * returning a @p GHashTable. * - @c AtspiWrapperBase::strHashMapInvoke(): identical to @c AtspiWrapperBase::hashMapInvoke() * using C strings for keys and values. * . * If none of those match the exact return type of the C call to wrap, use * @c AtspiWrapperBase::invokeThrow() or even @c GObjectWrapperBase::invoke(), and manually * manage the result value. You can use Atspi::gmem functions to help. Basically the idea is * that you always return a self-managing object to make memory management easy (whereas it's * obnoxiously hard with plain C Atspi API).
*/
namespace Atspi
{ /** @brief Helpers for managing GLib memory in a more C++-style */ namespace gmem
{ /** @brief Wraps a pointer to free with @c g_free() in a @c std::unique_ptr */ template <typename T> staticinlineauto unique_gmem(T* ptr)
{ return std::unique_ptr<T, decltype(&g_free)>(ptr, &g_free);
}
/** @brief Wraps a @c GArray to free with @c g_array_unref() in a @c std::unique_ptr */ staticinlineauto unique_garray(GArray* p)
{ return std::unique_ptr<GArray, decltype(&g_array_unref)>(p, &g_array_unref);
}
/** @brief Wraps a @c GHashTable to free with @c g_hash_table_unref() in a @c std::unique_ptr */ staticinlineauto unique_ghashtable(GHashTable* p)
{ return std::unique_ptr<GHashTable, decltype(&g_hash_table_unref)>(p, &g_hash_table_unref);
}
}
// --- GObject cast wrappers based on type: usage is cast<AtspiAccessible*>(pCInstance) #define DEFINE_GOBJECT_CAST(CType, GType) \ template <typename P, typename T, std::enable_if_t<std::is_same_v<P, CType*>, int> = 1> \
P cast(T* pInstance) \
{ \ return G_TYPE_CHECK_INSTANCE_CAST(pInstance, GType, std::remove_pointer_t<P>); \
}
#undef DEFINE_GOBJECT_CAST // --- end GObject cast wrappers
class GLibEnumBase
{ protected: /** * @brief Retrieves the string representation of an enumeration value * @param gt The @c GType for the enumeration * @param value The enumeration value for which to get the name for * @param fallback Fallback value in case @p values falls outside the enumeration * @returns A string representing @p value
*/ static std::string glibEnumValueName(GType gt, gint value,
std::string_view fallback = "unknown")
{ auto klass = static_cast<GEnumClass*>(g_type_class_ref(gt)); auto enum_value = g_enum_get_value(klass, value);
std::string ret(enum_value ? enum_value->value_name : fallback);
g_type_class_unref(klass); return ret;
}
};
class Role : private GLibEnumBase
{ public: static std::string getName(AtspiRole role)
{ return glibEnumValueName(atspi_role_get_type(), role);
}
};
class State : private GLibEnumBase
{ public: static std::string getName(AtspiStateType state)
{ return glibEnumValueName(atspi_state_type_get_type(), state);
}
};
/** * @brief Base class for GObject wrappers * * This leverages std::shared_ptr as a cheap way of wrapping a raw pointer, and its deleter as a * mean of using g_object_unref() to cleanup. This is sub-optimal as it maintains a separate * refcount to the GObject one, but it's easy.
*/ template <class T> class GObjectWrapperBase : public std::shared_ptr<T>
{ public: /* this is the boundary of C++ type safety, so we can have inheritance working * properly with the C types. This should still be safe as it uses cast() which should be
* defined for each using type with DEFINE_GOBJECT_CAST(), which uses GType validation */ template <typename P = T*> P get() const { return cast<P>(std::shared_ptr<T>::get()); }
protected: /** * @brief Calls the C function @p f on the C object wrapped by @p this * @param f The C function to call * @param args Additional arguments to @p f * @returns The return value from @p f * * Calls the C function @p f similar to <tt>f(get(), args...)</tt>. Care is taken of * transforming @c get() to the type actually expected as the first argument of @p f, using * @c get<TypeOfFsFirstArgument>(), which performs a runtime verification of the conversion. * * @note The type verification on whether @p f actually takes what get() returns is performed * at runtime, so there will be no compilation error or warning if trying to use an * incompatible C function. A check will however be performed at runtime, at least * helping diagnose a possible invalid conversion.
*/ template <typename F, typename... Ts> inlineauto invoke(F f, Ts... args) const
{ using FT = std::remove_pointer_t<F>; constauto p = get<typename boost::function_traits<FT>::arg1_type>(); return f(p, args...);
}
private: staticvoid deleter(T* p)
{ if (p)
g_object_unref(p);
}
public: /** * @param pObj The raw GObject to wrap * @param takeRef Whether to take ownership of the object or not. If set to @c false, it will * call @c g_object_ref(pAcc) to acquire a new reference to the GObject.
*/
GObjectWrapperBase(T* pObj = nullptr, bool takeRef = true)
: std::shared_ptr<T>(pObj, deleter)
{ if (pObj && !takeRef)
g_object_ref(pObj);
}
};
/** @brief AtspiStateSet C++ wrapper */ class StateSet : public GObjectWrapperBase<AtspiStateSet>
{ public: using GObjectWrapperBase::GObjectWrapperBase;
/** @brief AtspiRelation C++ wrapper */ class Relation : public GObjectWrapperBase<AtspiRelation>
{ public: using GObjectWrapperBase::GObjectWrapperBase;
/* intermediate base just for splitting out the *invoke* helpers implementations, so the actual
* user-targeted class can hold only the actual API */ template <class T> class AtspiWrapperBase : public GObjectWrapperBase<T>
{ protected: using GObjectWrapperBase<T>::invoke;
/** * @brief Calls the throwing C function @p f on the C object wrapped by @p this * @param f The C function to call * @param args Additional arguments to @p f * @returns The raw return value from @p f * @throws css::uno::RuntimeException Exception @c GError are translated to * * This wrapper calls @p f with parameters @p args and an additional @c GError parameter to * catch C exception, transforming them into C++ exceptions of type * @c css::uno::RuntimeException. * * @see invoke()
*/ template <typename F, typename... Ts> inlineauto invokeThrow(F f, Ts... args) const
{
GError* err = nullptr; auto ret = invoke(f, args..., &err); if (err)
{ throw css::uno::RuntimeException(OUString::fromUtf8(err->message));
} return ret;
}
/** * @brief Calls the throwing C function @p f on the C object wrapped by @p this and returns a string * @param f The C function to call * @param args Additional arguments to @p f * @tparam E the type of exception to throw if @p f returns @c null, defaults to * @c css::uno::RuntimeException * @returns A string holding the return value from @p f * @throws css::uno::RuntimeException See invokeThrow() * @throws E Exception to use if @p f returns null with no other error * * Just like @c invokeThrow(), but wraps the return value in an @c std::string and manages the * lifetime of the C function return value. * * As @c std::string cannot represent a @c null value, if @p f returned such a value without * throwing an exception, this method will throw an exception of type @p E * * @see invokeThrow()
*/ template <typename E = css::uno::RuntimeException, typename F, typename... Ts> inline std::string strInvoke(F f, Ts... args) const
{ auto r = invokeThrow(f, args...);
/* if the API returned NULL without throwing, use the specified exception because a nullptr
* std::string is not valid, and std::logic_error isn't gonna be very useful to the caller */ if (!r) throw E();
return gmem::unique_gmem(r).get();
}
/** * @brief Calls the throwing C function @p f on the C object wrapped by @p this and returns a vector * @param f The C function to call * @param args Additional arguments to @p f * @tparam Vi The type of the members of the @c GArray @p f returns * @tparam Vo The type of the members of the returned vector * @returns A vector holding the return value from @p f * @throws css::uno::RuntimeException See invokeThrow() * * Just like @c invokeThrow(), but wraps the return in an @c std::vector<Vo> and manages the * lifetime of the C function return value. * * @p Vi has to be implicitly convertible to @p Vo. A typical usage could be * <tt>garrayInvoke<AtspiAccessible*, Accessible>(...)</tt>, which would transform a @c GArray * of @c AtspiAccessible* to an @c std::vector of @c Atspi::Accessible. * * @warning You have to get @p Vi right, there is no way to validate this type is correct or * not, so you won't get a compilation error nor even a warning if you give the wrong * type here. * * @see invokeThrow()
*/ template <typename Vi, typename Vo, typename F, typename... Ts> inline std::vector<Vo> garrayInvoke(F f, Ts... args) const
{ auto garray = gmem::unique_garray(invokeThrow(f, args...));
std::vector<Vo> vec; for (auto i = decltype(garray->len){ 0 }; i < garray->len; i++)
vec.push_back(g_array_index(garray, Vi, i)); return vec;
}
/** * @brief Wraps an AT-SPI call returning a GHashTable * @tparam Ki Type of the keys in the wrapped hash table * @tparam Vi Type of the values in the wrapped hash table * @tparam Ko Type of the keys in the wrapper map (this must be convertible from Ki) * @tparam Vo Type of the values in the wrapper map (this must be convertible from Kv) * @param f The function to call * @param args Arguments to pass to @p f * @returns A @c std::unordered_map holding the data returned by @p f. * * @see invokeThrow() * @see strHashMapInvoke()
*/ template <typename Ki, typename Vi, typename Ko, typename Vo, typename F, typename... Ts> inline std::unordered_map<Ko, Vo> hashMapInvoke(F f, Ts... args) const
{ auto ghash = gmem::unique_ghashtable(invokeThrow(f, args...));
std::unordered_map<Ko, Vo> map;
GHashTableIter iter;
g_hash_table_iter_init(&iter, ghash.get());
gpointer key, value; while (g_hash_table_iter_next(&iter, &key, &value))
{
map.emplace(static_cast<Ki>(key), static_cast<Vi>(value));
} return map;
}
/** * @brief Just like @c hashMapInvoke() but already specialized for strings * @param f The C function to call * @param args Arguments to @p f * @returns A @c std::unordered_map holding the data returned by @p f * * This is exactly the same as * <tt>hashMapInvoke<gchar*, gchar*, std::string, std::string>(f, args...)</tt>
*/ template <typename F, typename... Ts> inlineauto strHashMapInvoke(F f, Ts... args) const
{ return hashMapInvoke<gchar*, gchar*, std::string, std::string>(f, args...);
}
public: using GObjectWrapperBase<T>::GObjectWrapperBase;
};
class Component; class Text;
/** * @brief AtspiAccessible C++ wrapper * * This is a wrapper for the AtspiAccessible GObject class to make it a bit nicer to use in C++, * including a proper class with methods, regular exceptions, easy memory management, and an * iterator to enumerate children. * * As this class actually inherits from std::shared_ptr, you can easily use @c get() to retrieve * the wrapped pointer in case you need to, e.g. if some specific API is missing. However, take * care of memory management on that object not to have it destroyed early (e.g. don't let anyone * call @c g_object_unref() on it if they didn't call g_object_ref() first). * * To use it, just wrap an initial AtspiAccessible using the class constructor. * @code * Atspi::Accessible desktop(atspi_get_desktop(0)); * for (auto&& app: desktop) { * std::cout << app->getName() << std::endl; * } * @endcode * * For details on the specific methods, see the C Atspi documentation.
*/ class Accessible : public AtspiWrapperBase<AtspiAccessible>
{ public: using AtspiWrapperBase<AtspiAccessible>::AtspiWrapperBase;
private: template <class I, typename F> I queryInterface(F f) const
{ auto pIface = invoke(f); if (pIface) return I(pIface); throw css::uno::RuntimeException("Not implemented");
}
public:
Component queryComponent() const;
Text queryText() const;
// convenience extensions class iterator
{ public: using iterator_category = std::input_iterator_tag; using value_type = Accessible; using difference_type = std::ptrdiff_t; using reference = value_type&; using pointer = value_type*;
private: const Accessible* m_pAccessible; int m_idx;
/** @brief AtspiText C++ wrapper */ class Text : public Accessible
{ public:
Text(AtspiText* pObj = nullptr, bool takeRef = true)
: Accessible(cast<AtspiAccessible*>(pObj), takeRef)
{
}
/** Wrapper for AtspiRange * * This is not actually required, but helps make this more C++-y (by allowing TextRange to
* inherit it) and more LibreOffice-y (by having camelCase names) */ struct Range
{ int startOffset; int endOffset;
Range(int startOffset_, int endOffset_)
: startOffset(startOffset_)
, endOffset(endOffset_)
{
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.