diff options
-rw-r--r-- | DOCS/client-api-changes.rst | 1 | ||||
-rw-r--r-- | libmpv/client.h | 2 | ||||
-rw-r--r-- | libmpv/qthelper.hpp | 234 | ||||
-rw-r--r-- | wscript_build.py | 2 |
4 files changed, 237 insertions, 2 deletions
diff --git a/DOCS/client-api-changes.rst b/DOCS/client-api-changes.rst index b1274cad7b..a72bf9e398 100644 --- a/DOCS/client-api-changes.rst +++ b/DOCS/client-api-changes.rst @@ -25,6 +25,7 @@ API changes :: + 1.8 - add qthelper.hpp 1.7 - add mpv_command_node(), mpv_command_node_async() 1.6 - modify "core-idle" property behavior - MPV_EVENT_LOG_MESSAGE now always sends complete lines diff --git a/libmpv/client.h b/libmpv/client.h index 084f6e6e6c..7f545866aa 100644 --- a/libmpv/client.h +++ b/libmpv/client.h @@ -162,7 +162,7 @@ extern "C" { * relational operators (<, >, <=, >=). */ #define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL) -#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 7) +#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 8) /** * Return the MPV_CLIENT_API_VERSION the mpv source has been compiled with. diff --git a/libmpv/qthelper.hpp b/libmpv/qthelper.hpp new file mode 100644 index 0000000000..07bf194a1d --- /dev/null +++ b/libmpv/qthelper.hpp @@ -0,0 +1,234 @@ +/* Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * Note: these helpers are provided for convenience for C++/Qt applications. + * This is based on the public API in client.h, and it does not encode any + * knowledge that is not known or guaranteed outside of the C client API. You + * can even copy and modify this code as you like, or implement similar things + * for other languages. + */ + +#include <cstring> + +#include <QVariant> +#include <QString> +#include <QList> +#include <QHash> + +#include <mpv/client.h> + +namespace mpv { +namespace qt { + +static inline QVariant node_to_variant(const mpv_node *node) +{ + switch (node->format) { + case MPV_FORMAT_STRING: + return QVariant(QString::fromUtf8(node->u.string)); + case MPV_FORMAT_FLAG: + return QVariant(static_cast<bool>(node->u.flag)); + case MPV_FORMAT_INT64: + return QVariant(static_cast<qlonglong>(node->u.int64)); + case MPV_FORMAT_DOUBLE: + return QVariant(node->u.double_); + case MPV_FORMAT_NODE_ARRAY: { + mpv_node_list *list = node->u.list; + QVariantList qlist; + for (int n = 0; n < list->num; n++) + qlist.append(node_to_variant(&list->values[n])); + return QVariant(qlist); + } + case MPV_FORMAT_NODE_MAP: { + mpv_node_list *list = node->u.list; + QVariantMap qmap; + for (int n = 0; n < list->num; n++) { + qmap.insert(QString::fromUtf8(list->keys[n]), + node_to_variant(&list->values[n])); + } + return QVariant(qmap); + } + default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions) + return QVariant(); + } +} + +struct node_builder { + node_builder(const QVariant& v) { + set(&node_, v); + } + ~node_builder() { + free_node(&node_); + } + mpv_node *node() { return &node_; } +private: + Q_DISABLE_COPY(node_builder) + //node_builder(node_builder const&); // disallow + //node_builder& operator=(node_builder const&); // disallow + mpv_node node_; + mpv_node_list *create_list(mpv_node *dst, bool is_map, int num) { + dst->format = is_map ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY; + mpv_node_list *list = new mpv_node_list(); + dst->u.list = list; + if (!list) + goto err; + list->values = new mpv_node[num](); + if (!list->values) + goto err; + if (is_map) { + list->keys = new char*[num](); + if (!list->keys) + goto err; + } + return list; + err: + free_node(dst); + return NULL; + } + char *dup_qstring(const QString &s) { + QByteArray b = s.toUtf8(); + char *r = new char[b.size() + 1]; + if (r) + std::memcpy(r, b.data(), b.size() + 1); + return r; + } + bool test_type(const QVariant &v, QMetaType::Type t) { + // The Qt docs say: "Although this function is declared as returning + // "QVariant::Type(obsolete), the return value should be interpreted + // as QMetaType::Type." + // So a cast really seems to be needed to avoid warnings (urgh). + return static_cast<int>(v.type()) == static_cast<int>(t); + } + void set(mpv_node *dst, const QVariant &src) { + if (test_type(src, QMetaType::QString)) { + dst->format = MPV_FORMAT_STRING; + dst->u.string = dup_qstring(src.toString()); + if (!dst->u.string) + goto fail; + } else if (test_type(src, QMetaType::Bool)) { + dst->format = MPV_FORMAT_FLAG; + dst->u.flag = src.toBool() ? 1 : 0; + } else if (test_type(src, QMetaType::Int) || + test_type(src, QMetaType::LongLong) || + test_type(src, QMetaType::UInt) || + test_type(src, QMetaType::ULongLong)) + { + dst->format = MPV_FORMAT_INT64; + dst->u.int64 = src.toLongLong(); + } else if (test_type(src, QMetaType::Double)) { + dst->format = MPV_FORMAT_DOUBLE; + dst->u.double_ = src.toDouble(); + } else if (src.canConvert<QVariantList>()) { + QVariantList qlist = src.toList(); + mpv_node_list *list = create_list(dst, false, qlist.size()); + if (!list) + goto fail; + for (int n = 0; n < qlist.size(); n++) + set(&list->values[n], qlist[n]); + } else if (src.canConvert<QVariantMap>()) { + QVariantMap qmap = src.toMap(); + mpv_node_list *list = create_list(dst, true, qmap.size()); + if (!list) + goto fail; + for (int n = 0; n < qmap.size(); n++) { + list->keys[n] = dup_qstring(qmap.keys()[n]); + if (!list->keys[n]) { + free_node(dst); + goto fail; + } + set(&list->values[n], qmap.values()[n]); + } + } else { + goto fail; + } + return; + fail: + dst->format = MPV_FORMAT_NONE; + } + void free_node(mpv_node *dst) { + switch (dst->format) { + case MPV_FORMAT_STRING: + delete[] dst->u.string; + break; + case MPV_FORMAT_NODE_ARRAY: + case MPV_FORMAT_NODE_MAP: { + mpv_node_list *list = dst->u.list; + if (list) { + for (int n = 0; n < list->num; n++) { + if (list->keys) + delete list->keys[n]; + if (list->values) + free_node(&list->values[n]); + } + } + delete[] list->keys; + delete[] list->values; + delete list; + break; + } + default: ; + } + dst->format = MPV_FORMAT_NONE; + } +}; + +/** + * RAII wrapper that calls mpv_free_node_contents() on the pointer. + */ +struct node_autofree { + mpv_node *ptr; + node_autofree(mpv_node *a_ptr) : ptr(a_ptr) {} + ~node_autofree() { mpv_free_node_contents(ptr); } +}; + +/** + * Return the given property as mpv_node converted to QVariant, or QVariant() + * on error. + * + * @param name the property name + */ +static inline QVariant get_property_variant(mpv_handle *ctx, const QString &name) +{ + mpv_node node; + if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0) + return QVariant(); + node_autofree f(&node); + return node_to_variant(&node); +} + +/** + * Set the given property as mpv_node converted from the QVariant argument. + */ +static inline int set_property_variant(mpv_handle *ctx, const QString &name, + const QVariant &v) +{ + node_builder node(v); + return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); +} + +/** + * mpv_command_node() equivalent. Returns QVariant() on error (and + * unfortunately, the same on success). + */ +static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args) +{ + node_builder node(args); + mpv_node res; + if (mpv_command_node(ctx, node.node(), &res) < 0) + return QVariant(); + node_autofree f(&res); + return node_to_variant(&res); +} + +} +} diff --git a/wscript_build.py b/wscript_build.py index f7df1c5aad..3f2cc68ffc 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -505,7 +505,7 @@ def build(ctx): PRIV_LIBS = get_deps(), ) - headers = ["client.h"] + headers = ["client.h", "qthelper.hpp"] for f in headers: ctx.install_as(ctx.env.INCDIR + '/mpv/' + f, 'libmpv/' + f) |