From b2e1cb490c415ae871cabe7731a58b1914b588a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Sun, 24 Apr 2022 13:43:33 +0200 Subject: [PATCH] Update pybind11 bindings. --- .clang-format | 1 + src/runner-pybind11/CMakeLists.txt | 2 + src/runner-pybind11/converters-old.h | 1062 +++++++------- src/runner-pybind11/error.h | 8 +- src/runner-pybind11/gamefeatureswrappers.cpp | 146 +- src/runner-pybind11/gamefeatureswrappers.h | 180 +-- src/runner-pybind11/gilock.cpp | 6 +- src/runner-pybind11/gilock.h | 13 +- src/runner-pybind11/proxypluginwrappers.cpp | 467 ++++--- src/runner-pybind11/proxypluginwrappers.h | 409 +++--- .../pybind11_qt/details/pybind11_qt_enum.h | 8 +- .../pybind11_qt/details/pybind11_qt_qmap.h | 9 +- .../pybind11_qt/details/pybind11_qt_sip.cpp | 9 +- .../pybind11_qt/details/pybind11_qt_sip.h | 30 +- .../pybind11_qt/details/pybind11_qt_utils.cpp | 3 +- .../pybind11_qt/details/pybind11_qt_utils.h | 9 +- src/runner-pybind11/pybind11_qt/pybind11_qt.h | 35 + .../pybind11_qt/pybind11_qt_basic.cpp | 46 +- .../pybind11_qt/pybind11_qt_basic.h | 6 +- .../pybind11_qt/pybind11_qt_enums.h | 25 +- .../pybind11_qt/pybind11_qt_objects.h | 26 +- .../pybind11_qt/pybind11_qt_qflags.h | 8 +- src/runner-pybind11/pyfiletree.h | 36 - src/runner-pybind11/pythonrunner.cpp | 1224 +---------------- src/runner-pybind11/pythonrunner.h | 25 +- src/runner-pybind11/pythonutils.cpp | 35 +- src/runner-pybind11/pythonutils.h | 5 +- src/runner-pybind11/pythonwrapperutilities.h | 35 +- src/runner-pybind11/shared_ptr_converter.h | 216 +-- src/runner-pybind11/uibasewrappers.h | 68 - src/runner-pybind11/widgets.cpp | 66 - src/runner-pybind11/widgets.h | 9 - .../wrappers/basic_classes.cpp | 759 ++++++++++ .../wrappers/game_features.cpp | 19 + src/runner-pybind11/wrappers/plugins.cpp | 390 ++++++ .../{ => wrappers}/pyfiletree.cpp | 61 +- src/runner-pybind11/wrappers/pyfiletree.h | 36 + .../wrappers/uibase_wrappers.h | 74 - src/runner-pybind11/wrappers/widgets.cpp | 79 ++ src/runner-pybind11/wrappers/wrappers.cpp | 115 ++ src/runner-pybind11/wrappers/wrappers.h | 51 + 41 files changed, 3071 insertions(+), 2740 deletions(-) delete mode 100644 src/runner-pybind11/pyfiletree.h delete mode 100644 src/runner-pybind11/uibasewrappers.h delete mode 100644 src/runner-pybind11/widgets.cpp delete mode 100644 src/runner-pybind11/widgets.h create mode 100644 src/runner-pybind11/wrappers/basic_classes.cpp create mode 100644 src/runner-pybind11/wrappers/game_features.cpp create mode 100644 src/runner-pybind11/wrappers/plugins.cpp rename src/runner-pybind11/{ => wrappers}/pyfiletree.cpp (87%) create mode 100644 src/runner-pybind11/wrappers/pyfiletree.h delete mode 100644 src/runner-pybind11/wrappers/uibase_wrappers.h create mode 100644 src/runner-pybind11/wrappers/widgets.cpp create mode 100644 src/runner-pybind11/wrappers/wrappers.cpp create mode 100644 src/runner-pybind11/wrappers/wrappers.h diff --git a/.clang-format b/.clang-format index 70c5760..c040316 100644 --- a/.clang-format +++ b/.clang-format @@ -18,3 +18,4 @@ SpacesBeforeTrailingComments: 2 NamespaceIndentation: All MaxEmptyLinesToKeep: 1 BreakBeforeBraces: Stroustrup +ColumnLimit: 88 diff --git a/src/runner-pybind11/CMakeLists.txt b/src/runner-pybind11/CMakeLists.txt index 87e175c..0ff7f29 100644 --- a/src/runner-pybind11/CMakeLists.txt +++ b/src/runner-pybind11/CMakeLists.txt @@ -22,6 +22,8 @@ target_include_directories(pythonrunner PRIVATE ${PYTHON_ROOT}/Include) target_link_directories(pythonrunner PRIVATE ${Boost_LIBRARY_DIRS}) +target_compile_options(pythonrunner + PRIVATE $<$:/MP>) target_compile_definitions(pythonrunner PRIVATE QT_NO_KEYWORDS PYTHONRUNNER_LIBRARY) mo2_install_target(pythonrunner INSTALLDIR bin/plugins/data) diff --git a/src/runner-pybind11/converters-old.h b/src/runner-pybind11/converters-old.h index 6434590..d941eaf 100644 --- a/src/runner-pybind11/converters-old.h +++ b/src/runner-pybind11/converters-old.h @@ -16,566 +16,654 @@ namespace utils { - namespace bpy = boost::python; + namespace bpy = boost::python; - namespace QString_converter { + namespace QString_converter { - /** - * We need this since sip does not expose QString but uses standard python str. - */ - struct QString_to_python_str - { - static PyObject* convert(const QString& str) { - // It's safer to explicitly convert to unicode as if we don't, this can return - // either str or unicode without it being easy to know which to expect - bpy::object pyStr = bpy::object(qUtf8Printable(str)); - if (SIPBytes_Check(pyStr.ptr())) - pyStr = pyStr.attr("decode")("utf-8"); - return bpy::incref(pyStr.ptr()); - } - }; + /** + * We need this since sip does not expose QString but uses standard python str. + */ + struct QString_to_python_str { + static PyObject* convert(const QString& str) + { + // It's safer to explicitly convert to unicode as if we don't, this can + // return either str or unicode without it being easy to know which to + // expect + bpy::object pyStr = bpy::object(qUtf8Printable(str)); + if (SIPBytes_Check(pyStr.ptr())) + pyStr = pyStr.attr("decode")("utf-8"); + return bpy::incref(pyStr.ptr()); + } + }; - struct QString_from_python_str - { + struct QString_from_python_str { - static void* convertible(PyObject* objPtr) { - return SIPBytes_Check(objPtr) || PyUnicode_Check(objPtr) ? objPtr : nullptr; - } + static void* convertible(PyObject* objPtr) + { + return SIPBytes_Check(objPtr) || PyUnicode_Check(objPtr) ? objPtr + : nullptr; + } - static void construct(PyObject* objPtr, bpy::converter::rvalue_from_python_stage1_data* data) { - // Ensure the string uses 8-bit characters - PyObject* strPtr = PyUnicode_Check(objPtr) ? PyUnicode_AsUTF8String(objPtr) : objPtr; + static void construct(PyObject* objPtr, + bpy::converter::rvalue_from_python_stage1_data* data) + { + // Ensure the string uses 8-bit characters + PyObject* strPtr = + PyUnicode_Check(objPtr) ? PyUnicode_AsUTF8String(objPtr) : objPtr; - // Extract the character data from the python string - const char* value = SIPBytes_AsString(strPtr); - assert(value != nullptr); + // Extract the character data from the python string + const char* value = SIPBytes_AsString(strPtr); + assert(value != nullptr); - // allocate storage - void* storage = ((bpy::converter::rvalue_from_python_storage*)data)->storage.bytes; + // allocate storage + void* storage = + ((bpy::converter::rvalue_from_python_storage*)data) + ->storage.bytes; - // construct QString in the allocated memory - new (storage) QString(value); + // construct QString in the allocated memory + new (storage) QString(value); - data->convertible = storage; + data->convertible = storage; - // Deallocate local copy if one was made - if (strPtr != objPtr) - Py_DecRef(strPtr); - } - }; + // Deallocate local copy if one was made + if (strPtr != objPtr) + Py_DecRef(strPtr); + } + }; - } + } // namespace QString_converter - namespace Enum_converter { + namespace Enum_converter { - /** - * - */ - template - struct Enum_to_int - { - static PyObject* convert(const Enum& flags) { - return bpy::incref(bpy::object(static_cast(flags)).ptr()); - } - }; + /** + * + */ + template + struct Enum_to_int { + static PyObject* convert(const Enum& flags) + { + return bpy::incref(bpy::object(static_cast(flags)).ptr()); + } + }; - template - struct Enum_from_python_obj - { + template + struct Enum_from_python_obj { - static void* convertible(PyObject* objPtr) { - return SIPLong_Check(objPtr) ? objPtr : nullptr; - } + static void* convertible(PyObject* objPtr) + { + return SIPLong_Check(objPtr) ? objPtr : nullptr; + } - static void construct(PyObject* objPtr, bpy::converter::rvalue_from_python_stage1_data* data) { - int intVersion = (int)SIPLong_AsLong(objPtr); - void* storage = ((bpy::converter::rvalue_from_python_storage*)data)->storage.bytes; - new (storage) Enum(static_cast(intVersion)); - data->convertible = storage; - } - }; + static void construct(PyObject* objPtr, + bpy::converter::rvalue_from_python_stage1_data* data) + { + int intVersion = (int)SIPLong_AsLong(objPtr); + void* storage = + ((bpy::converter::rvalue_from_python_storage*)data) + ->storage.bytes; + new (storage) Enum(static_cast(intVersion)); + data->convertible = storage; + } + }; - } + } // namespace Enum_converter - namespace QFlags_converter { + namespace QFlags_converter { - /** - * - */ - template - struct QFlags_to_int - { - static PyObject* convert(const QFlags& flags) { - return bpy::incref(bpy::object(static_cast(flags)).ptr()); - } - }; + /** + * + */ + template + struct QFlags_to_int { + static PyObject* convert(const QFlags& flags) + { + return bpy::incref(bpy::object(static_cast(flags)).ptr()); + } + }; - template - struct QFlags_from_python_obj - { + template + struct QFlags_from_python_obj { - static void* convertible(PyObject* objPtr) { - return SIPLong_Check(objPtr) ? objPtr : nullptr; - } + static void* convertible(PyObject* objPtr) + { + return SIPLong_Check(objPtr) ? objPtr : nullptr; + } - static void construct(PyObject* objPtr, bpy::converter::rvalue_from_python_stage1_data* data) { - int intVersion = (int)SIPLong_AsLong(objPtr); - T tVersion = (T)intVersion; - void* storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; - new (storage) QFlags(tVersion); + static void construct(PyObject* objPtr, + bpy::converter::rvalue_from_python_stage1_data* data) + { + int intVersion = (int)SIPLong_AsLong(objPtr); + T tVersion = (T)intVersion; + void* storage = + ((bpy::converter::rvalue_from_python_storage>*)data) + ->storage.bytes; + new (storage) QFlags(tVersion); - data->convertible = storage; - } - }; + data->convertible = storage; + } + }; - } + } // namespace QFlags_converter - namespace QVariant_converter { + namespace QVariant_converter { - struct QVariant_to_python_obj - { - static PyObject* convert(const QVariant& var) { - switch (var.type()) { - case QVariant::Invalid: return bpy::incref(Py_None); - case QVariant::Int: return SIPLong_FromLong(var.toInt()); - case QVariant::UInt: return PyLong_FromUnsignedLong(var.toUInt()); - case QVariant::Bool: return PyBool_FromLong(var.toBool()); - case QVariant::String: return bpy::incref(bpy::object(var.toString()).ptr()); - // We need to check for StringList here because these are not considered List - // since List is QList will StringList is QList: - case QVariant::StringList: return bpy::incref(bpy::object(var.toStringList()).ptr()); - case QVariant::List: { - return bpy::incref(bpy::object(var.toList()).ptr()); - } break; - case QVariant::Map: { - return bpy::incref(bpy::object(var.toMap()).ptr()); - } break; - default: { - PyErr_Format(PyExc_TypeError, "type unsupported: %d", var.type()); - throw bpy::error_already_set(); - } break; - } - } - }; + struct QVariant_to_python_obj { + static PyObject* convert(const QVariant& var) + { + switch (var.type()) { + case QVariant::Invalid: + return bpy::incref(Py_None); + case QVariant::Int: + return SIPLong_FromLong(var.toInt()); + case QVariant::UInt: + return PyLong_FromUnsignedLong(var.toUInt()); + case QVariant::Bool: + return PyBool_FromLong(var.toBool()); + case QVariant::String: + return bpy::incref(bpy::object(var.toString()).ptr()); + // We need to check for StringList here because these are not considered + // List since List is QList will StringList is QList: + case QVariant::StringList: + return bpy::incref(bpy::object(var.toStringList()).ptr()); + case QVariant::List: { + return bpy::incref(bpy::object(var.toList()).ptr()); + } break; + case QVariant::Map: { + return bpy::incref(bpy::object(var.toMap()).ptr()); + } break; + default: { + PyErr_Format(PyExc_TypeError, "type unsupported: %d", var.type()); + throw bpy::error_already_set(); + } break; + } + } + }; - struct QVariant_from_python_obj - { + struct QVariant_from_python_obj { - static void* convertible(PyObject* objPtr) { - if (!SIPBytes_Check(objPtr) && !PyUnicode_Check(objPtr) && !PyLong_Check(objPtr) && - !PyBool_Check(objPtr) && !PyList_Check(objPtr) && !PyDict_Check(objPtr) && - objPtr != Py_None) { - return nullptr; - } - return objPtr; - } + static void* convertible(PyObject* objPtr) + { + if (!SIPBytes_Check(objPtr) && !PyUnicode_Check(objPtr) && + !PyLong_Check(objPtr) && !PyBool_Check(objPtr) && + !PyList_Check(objPtr) && !PyDict_Check(objPtr) && + objPtr != Py_None) { + return nullptr; + } + return objPtr; + } - template - static void constructVariant(const T& value, bpy::converter::rvalue_from_python_stage1_data* data) { - void* storage = ((bpy::converter::rvalue_from_python_storage*)data)->storage.bytes; + template + static void + constructVariant(const T& value, + bpy::converter::rvalue_from_python_stage1_data* data) + { + void* storage = + ((bpy::converter::rvalue_from_python_storage*)data) + ->storage.bytes; - new (storage) QVariant(value); + new (storage) QVariant(value); - data->convertible = storage; - } + data->convertible = storage; + } - static void constructVariant(bpy::converter::rvalue_from_python_stage1_data* data) { - void* storage = ((bpy::converter::rvalue_from_python_storage*)data)->storage.bytes; + static void + constructVariant(bpy::converter::rvalue_from_python_stage1_data* data) + { + void* storage = + ((bpy::converter::rvalue_from_python_storage*)data) + ->storage.bytes; - new (storage) QVariant(); + new (storage) QVariant(); - data->convertible = storage; - } + data->convertible = storage; + } - static void construct(PyObject* objPtr, bpy::converter::rvalue_from_python_stage1_data* data) { - if (PyList_Check(objPtr)) { - // We could check if all the elements can be converted to QString and store a QStringList - // in the QVariant but I am not sure that is really useful. - constructVariant(bpy::extract(objPtr)(), data); - } - else if (objPtr == Py_None) { - constructVariant(data); - } - else if (PyDict_Check(objPtr)) { - constructVariant(bpy::extract(objPtr)(), data); - } - else if (SIPBytes_Check(objPtr) || PyUnicode_Check(objPtr)) { - constructVariant(bpy::extract(objPtr)(), data); - } - // PyBools will also return true for SIPLong_Check but not the other way around, so the order - // here is relevant. - else if (PyBool_Check(objPtr)) { - constructVariant(bpy::extract(objPtr)(), data); - } - else if (SIPLong_Check(objPtr)) { - // QVariant doesn't have long. It has int or long long. Given that on m/s, - // long is 32 bits for 32- and 64- bit code... - constructVariant(bpy::extract(objPtr)(), data); - } - else { - PyErr_SetString(PyExc_TypeError, "type unsupported"); - throw bpy::error_already_set(); - } - } - }; + static void construct(PyObject* objPtr, + bpy::converter::rvalue_from_python_stage1_data* data) + { + if (PyList_Check(objPtr)) { + // We could check if all the elements can be converted to QString + // and store a QStringList in the QVariant but I am not sure that is + // really useful. + constructVariant(bpy::extract(objPtr)(), data); + } + else if (objPtr == Py_None) { + constructVariant(data); + } + else if (PyDict_Check(objPtr)) { + constructVariant(bpy::extract(objPtr)(), data); + } + else if (SIPBytes_Check(objPtr) || PyUnicode_Check(objPtr)) { + constructVariant(bpy::extract(objPtr)(), data); + } + // PyBools will also return true for SIPLong_Check but not the other way + // around, so the order here is relevant. + else if (PyBool_Check(objPtr)) { + constructVariant(bpy::extract(objPtr)(), data); + } + else if (SIPLong_Check(objPtr)) { + // QVariant doesn't have long. It has int or long long. Given that + // on m/s, long is 32 bits for 32- and 64- bit code... + constructVariant(bpy::extract(objPtr)(), data); + } + else { + PyErr_SetString(PyExc_TypeError, "type unsupported"); + throw bpy::error_already_set(); + } + } + }; - } + } // namespace QVariant_converter - namespace QClass_converter { + namespace QClass_converter { - template struct MetaData; + template + struct MetaData; - template <> struct MetaData { static const char* className() { return "QObject"; } }; - template <> struct MetaData { static const char* className() { return "QWidget"; } }; - template <> struct MetaData { static const char* className() { return "QMainWindow"; } }; - template <> struct MetaData { static const char* className() { return "QDateTime"; } }; - template <> struct MetaData { static const char* className() { return "QDir"; } }; - template <> struct MetaData { static const char* className() { return "QFileInfo"; } }; - template <> struct MetaData { static const char* className() { return "QIcon"; } }; - template <> struct MetaData { static const char* className() { return "QSize"; } }; - template <> struct MetaData { static const char* className() { return "QUrl"; } }; - template <> struct MetaData { static const char* className() { return "QVariant"; } }; + template <> + struct MetaData { + static const char* className() { return "QObject"; } + }; + template <> + struct MetaData { + static const char* className() { return "QWidget"; } + }; + template <> + struct MetaData { + static const char* className() { return "QMainWindow"; } + }; + template <> + struct MetaData { + static const char* className() { return "QDateTime"; } + }; + template <> + struct MetaData { + static const char* className() { return "QDir"; } + }; + template <> + struct MetaData { + static const char* className() { return "QFileInfo"; } + }; + template <> + struct MetaData { + static const char* className() { return "QIcon"; } + }; + template <> + struct MetaData { + static const char* className() { return "QSize"; } + }; + template <> + struct MetaData { + static const char* className() { return "QUrl"; } + }; + template <> + struct MetaData { + static const char* className() { return "QVariant"; } + }; - template - struct QClass_converters - { - struct QClass_to_PyQt - { - template - static typename std::enable_if_t, T*> getSafeCopy(T* qClass) + template + struct QClass_converters { + struct QClass_to_PyQt { + template + static typename std::enable_if_t, T*> + getSafeCopy(T* qClass) + { + return new T(*qClass); + } + + template + static typename std::enable_if_t, T*> + getSafeCopy(T* qClass) + { + return qClass; + } + + static PyObject* convert(const T& object) + { + const sipTypeDef* type = + sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); + if (type == nullptr) { + return bpy::incref(Py_None); + } + + PyObject* sipObj = sipAPIAccess::sipAPI()->api_convert_from_type( + (void*)getSafeCopy((T*)&object), type, 0); + if (sipObj == nullptr) { + return bpy::incref(Py_None); + } + + if (std::is_copy_constructible_v) + // Ensure Python deletes the C++ component + sipAPIAccess::sipAPI()->api_transfer_back(sipObj); + + return bpy::incref(sipObj); + } + + static PyObject* convert(T* object) + { + if (object == nullptr) { + return bpy::incref(Py_None); + } + + const sipTypeDef* type = + sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); + if (type == nullptr) { + return bpy::incref(Py_None); + } + + PyObject* sipObj = sipAPIAccess::sipAPI()->api_convert_from_type( + getSafeCopy(object), type, 0); + if (sipObj == nullptr) { + return bpy::incref(Py_None); + } + + if (std::is_copy_constructible_v) + // Ensure Python deletes the C++ component + sipAPIAccess::sipAPI()->api_transfer_back(sipObj); + + return bpy::incref(sipObj); + } + + static PyObject* convert(const T* object) + { + return convert((T*)object); + } + + static PyTypeObject const* get_pytype() + { + const sipTypeDef* type = + sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); + if (type == nullptr) { + return bpy::incref(Py_None); + } + return bpy::incref(type->td_py_type); + } + }; + + static void* QClass_from_PyQt(PyObject* objPtr) + { + // This would transfer responsibility for deconstructing the object to + // C++, but Boost assumes l-value converters (such as this) don't do + // that Instead, this should be called within the wrappers for functions + // which return deletable pointers. + // sipAPI()->api_transfer_to(objPtr, Py_None); + if (PyObject_TypeCheck( + objPtr, sipAPIAccess::sipAPI()->api_simplewrapper_type)) { + sipSimpleWrapper* wrapper; + wrapper = reinterpret_cast(objPtr); + return wrapper->data; + } + else if (PyObject_TypeCheck(objPtr, + sipAPIAccess::sipAPI()->api_wrapper_type)) { + sipWrapper* wrapper; + wrapper = reinterpret_cast(objPtr); + return wrapper->super.data; + } + return nullptr; + } + }; + + } // namespace QClass_converter + + namespace details { + + inline bool has_arity(PyObject* object, std::size_t arity) { - return new T(*qClass); + // Mostly from https://stackoverflow.com/a/36143796/2666289 + bpy::object fn(bpy::handle<>(bpy::borrowed(object))); + + auto inspect = bpy::import("inspect"); + auto arg_spec = inspect.attr("getfullargspec")(fn); + bpy::object args = arg_spec.attr("args"), + varargs = arg_spec.attr("varargs"), + defaults = arg_spec.attr("defaults"); + + auto args_count = args ? bpy::len(args) : 0; + auto defaults_count = defaults ? bpy::len(defaults) : 0; + + if (static_cast(inspect.attr("ismethod")(fn)) && + fn.attr("__self__")) { + --args_count; + } + + auto required_count = args_count - defaults_count; + + return required_count <= + arity // Cannot require more parameters than given, + && + (args_count >= arity || varargs); // Must accept enough parameters. } - template - static typename std::enable_if_t, T*> getSafeCopy(T* qClass) + template + struct wrap_impl; + + template <> + struct wrap_impl<> { + template + static decltype(auto) apply(T&& t) + { + return std::forward(t); + } + }; + + template + struct wrap_impl, Ws...> : public wrap_impl<> { + using wrap_impl<>::apply; + + static auto apply(T t) { return boost::ref(t); } + }; + + template + struct wrap_impl, Ws...> + : public wrap_impl<> { + using wrap_impl<>::apply; + + static auto apply(T t) { return bpy::ptr(t); } + }; + + template + struct wrap_impl { + template + static decltype(auto) apply(T&& t) + { + return wrap::apply(std::forward(t)); + } + }; + + } // namespace details + + /** + * @brief Convert a python callable to a valid C++ Callable object. Also works + * for None. + */ + template + struct Functor_converter; + + template + struct Functor_converter { + + template + static decltype(auto) wrap(T&& t) { - return qClass; + return details::wrap_impl::apply(std::forward(t)); } - static PyObject* convert(const T& object) { - const sipTypeDef* type = sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); - if (type == nullptr) { - return bpy::incref(Py_None); - } + struct FunctorWrapper { + FunctorWrapper(boost::python::object callable) : m_Callable(callable) {} - PyObject* sipObj = sipAPIAccess::sipAPI()->api_convert_from_type((void*)getSafeCopy((T*)&object), type, 0); - if (sipObj == nullptr) { - return bpy::incref(Py_None); - } + ~FunctorWrapper() + { + GILock lock; + m_Callable = bpy::object(); + } - if (std::is_copy_constructible_v) - // Ensure Python deletes the C++ component - sipAPIAccess::sipAPI()->api_transfer_back(sipObj); + R operator()(Args... params) + { + GILock lock; + try { + if constexpr (std::is_same_v) { + m_Callable(wrap(params)...); + } + else { + return bpy::extract(m_Callable(wrap(params)...)); + } + } + catch (const boost::python::error_already_set&) { + throw pyexcept::PythonError(""); + } + catch (...) { + throw pyexcept::UnknownException(); + } + } - return bpy::incref(sipObj); + boost::python::object m_Callable; + }; + + static void* convertible(PyObject* object) + { + // We allow None here, we will just default-construct a std::function: + if (object == Py_None) { + return object; + } + + // Otherwize we check that we have a callable object: + if (!PyCallable_Check(object) || + !details::has_arity(object, sizeof...(Args))) { + return nullptr; + } + return object; } - static PyObject* convert(T* object) { - if (object == nullptr) { - return bpy::incref(Py_None); - } - - const sipTypeDef* type = sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); - if (type == nullptr) { - return bpy::incref(Py_None); - } - - PyObject* sipObj = sipAPIAccess::sipAPI()->api_convert_from_type(getSafeCopy(object), type, 0); - if (sipObj == nullptr) { - return bpy::incref(Py_None); - } - - if (std::is_copy_constructible_v) - // Ensure Python deletes the C++ component - sipAPIAccess::sipAPI()->api_transfer_back(sipObj); - - return bpy::incref(sipObj); + static void construct(PyObject* object, + bpy::converter::rvalue_from_python_stage1_data* data) + { + bpy::object callable(bpy::handle<>(bpy::borrowed(object))); + void* storage = ((bpy::converter::rvalue_from_python_storage< + std::function>*)data) + ->storage.bytes; + if (callable.is_none()) { + new (storage) std::function{}; + } + else { + new (storage) std::function(FunctorWrapper(callable)); + } + data->convertible = storage; } - - static PyObject* convert(const T* object) { - return convert((T*)object); - } - - static PyTypeObject const* get_pytype() { - const sipTypeDef* type = sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); - if (type == nullptr) { - return bpy::incref(Py_None); - } - return bpy::incref(type->td_py_type); - } - }; - - static void* QClass_from_PyQt(PyObject* objPtr) - { - // This would transfer responsibility for deconstructing the object to C++, but Boost assumes l-value converters (such as this) don't do that - // Instead, this should be called within the wrappers for functions which return deletable pointers. - //sipAPI()->api_transfer_to(objPtr, Py_None); - if (PyObject_TypeCheck(objPtr, sipAPIAccess::sipAPI()->api_simplewrapper_type)) { - sipSimpleWrapper* wrapper; - wrapper = reinterpret_cast(objPtr); - return wrapper->data; - } - else if (PyObject_TypeCheck(objPtr, sipAPIAccess::sipAPI()->api_wrapper_type)) { - sipWrapper* wrapper; - wrapper = reinterpret_cast(objPtr); - return wrapper->super.data; - } - return nullptr; - } }; - } + /** + * @brief Call policy that automatically downcast shared pointer of type FromType + * to shared pointer of type ToType. + */ + template + struct DowncastConverter { - namespace details { + bool convertible() const { return true; } - inline bool has_arity(PyObject* object, std::size_t arity) { - // Mostly from https://stackoverflow.com/a/36143796/2666289 - bpy::object fn(bpy::handle<>(bpy::borrowed(object))); + inline PyObject* operator()(std::shared_ptr p) const + { + if (p == nullptr) { + return bpy::detail::none(); + } + else { + auto downcast_p = std::dynamic_pointer_cast(p); + bpy::object p_value = + downcast_p == nullptr ? bpy::object{p} : bpy::object{downcast_p}; + return bpy::incref(p_value.ptr()); + } + } - auto inspect = bpy::import("inspect"); - auto arg_spec = inspect.attr("getfullargspec")(fn); - bpy::object args = arg_spec.attr("args"), - varargs = arg_spec.attr("varargs"), - defaults = arg_spec.attr("defaults"); - - auto args_count = args ? bpy::len(args) : 0; - auto defaults_count = defaults ? bpy::len(defaults) : 0; - - if (static_cast(inspect.attr("ismethod")(fn)) && fn.attr("__self__")) { - --args_count; - } - - auto required_count = args_count - defaults_count; - - return required_count <= arity // Cannot require more parameters than given, - && (args_count >= arity || varargs); // Must accept enough parameters. - } - - template - struct wrap_impl; - - template <> - struct wrap_impl<> { - template - static decltype(auto) apply(T&& t) { return std::forward(t); } + inline PyTypeObject const* get_pytype() const + { + return bpy::converter::registered_pytype::get_pytype(); + } }; - template - struct wrap_impl, Ws... > : public wrap_impl<> { - using wrap_impl<>::apply; + template + struct downcast_return { - static auto apply(T t) { - return boost::ref(t); - } + template + struct apply_; + + template + struct apply_> { + static_assert( + std::is_convertible_v, std::shared_ptr>); + using type = DowncastConverter; + }; + + template + using apply = apply_>; }; - template - struct wrap_impl, Ws... > : public wrap_impl<> { - using wrap_impl<>::apply; - - static auto apply(T t) { - return bpy::ptr(t); - } - }; - - template - struct wrap_impl { - template - static decltype(auto) apply(T&& t) { - return wrap::apply(std::forward(t)); - } - }; - - } - - /** - * @brief Convert a python callable to a valid C++ Callable object. Also works - * for None. - */ - template - struct Functor_converter; - - template - struct Functor_converter - { - - template - static decltype(auto) wrap(T&& t) { - return details::wrap_impl::apply(std::forward(t)); - } - - struct FunctorWrapper + // Functions: + inline void register_qstring_converter() { - FunctorWrapper(boost::python::object callable) : m_Callable(callable) { - } + using namespace QString_converter; + bpy::to_python_converter(); + bpy::converter::registry::push_back(&QString_from_python_str::convertible, + &QString_from_python_str::construct, + bpy::type_id()); + } - ~FunctorWrapper() { - GILock lock; - m_Callable = bpy::object(); - } - - R operator()(Args... params) { - GILock lock; - try { - if constexpr (std::is_same_v) { - m_Callable(wrap(params)... ); - } - else { - return bpy::extract(m_Callable(wrap(params)... )); - } - } - catch (const boost::python::error_already_set&) { - throw pyexcept::PythonError(""); - } - catch (...) { - throw pyexcept::UnknownException(); - } - } - - boost::python::object m_Callable; - }; - - static void* convertible(PyObject* object) + inline void register_qvariant_converter() { - // We allow None here, we will just default-construct a std::function: - if (object == Py_None) { - return object; - } - - // Otherwize we check that we have a callable object: - if (!PyCallable_Check(object) || !details::has_arity(object, sizeof...(Args))) { - return nullptr; - } - return object; + using namespace QVariant_converter; + bpy::to_python_converter(); + bpy::converter::registry::push_back(&QVariant_from_python_obj::convertible, + &QVariant_from_python_obj::construct, + bpy::type_id()); } - static void construct(PyObject* object, bpy::converter::rvalue_from_python_stage1_data* data) + template + inline void register_qflags_converter() { - bpy::object callable(bpy::handle<>(bpy::borrowed(object))); - void* storage =((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; - if (callable.is_none()) { - new (storage) std::function{}; - } - else { - new (storage) std::function(FunctorWrapper(callable)); - } - data->convertible = storage; - } - }; - - - - /** - * @brief Call policy that automatically downcast shared pointer of type FromType - * to shared pointer of type ToType. - */ - template - struct DowncastConverter { - - bool convertible() const { return true; } - - inline PyObject* operator()(std::shared_ptr p) const { - if (p == nullptr) { - return bpy::detail::none(); - } - else { - auto downcast_p = std::dynamic_pointer_cast(p); - bpy::object p_value = downcast_p == nullptr ? bpy::object{ p } : bpy::object{ downcast_p }; - return bpy::incref(p_value.ptr()); - } + using T = typename Flags::enum_type; + using namespace QFlags_converter; + bpy::to_python_converter>(); + bpy::converter::registry::push_back(&QFlags_from_python_obj::convertible, + &QFlags_from_python_obj::construct, + bpy::type_id()); } - inline PyTypeObject const* get_pytype() const { - return bpy::converter::registered_pytype::get_pytype(); + template + inline void register_enum_converter() + { + using namespace Enum_converter; + bpy::to_python_converter>(); + bpy::converter::registry::push_back(&Enum_from_python_obj::convertible, + &Enum_from_python_obj::construct, + bpy::type_id()); } - }; + template + inline void register_qclass_converter() + { + using Converter = QClass_converter::QClass_converters; + bpy::converter::registry::insert(&Converter::QClass_from_PyQt, + bpy::type_id()); + bpy::to_python_converter(); + bpy::to_python_converter(); + bpy::to_python_converter(); + } - template - struct downcast_return { + /** + * @brief Register a functor converter. + * + * @tparam Fn The function type to register. + * @tparam Wrappers... A list of wrapper (boost::python::pointer_wrapper or + * boost::reference_wrapper) indicating if parameters of the given (wrapped) type + * must be wrapped. + */ + template + inline void register_functor_converter() + { + using Converter = Functor_converter; + bpy::converter::registry::push_back(&Converter::convertible, + &Converter::construct, + bpy::type_id>()); + } - template - struct apply_; - - template - struct apply_> { - static_assert(std::is_convertible_v, std::shared_ptr>); - using type = DowncastConverter; - }; - - template - using apply = apply_>; - - }; - - // Functions: - inline void register_qstring_converter() { - using namespace QString_converter; - bpy::to_python_converter(); - bpy::converter::registry::push_back( - &QString_from_python_str::convertible, - &QString_from_python_str::construct, - bpy::type_id()); - } - - inline void register_qvariant_converter() { - using namespace QVariant_converter; - bpy::to_python_converter(); - bpy::converter::registry::push_back( - &QVariant_from_python_obj::convertible, - &QVariant_from_python_obj::construct, - bpy::type_id()); - } - - template - inline void register_qflags_converter() { - using T = typename Flags::enum_type; - using namespace QFlags_converter; - bpy::to_python_converter>(); - bpy::converter::registry::push_back( - &QFlags_from_python_obj::convertible, - &QFlags_from_python_obj::construct, - bpy::type_id()); - } - - template - inline void register_enum_converter() { - using namespace Enum_converter; - bpy::to_python_converter>(); - bpy::converter::registry::push_back( - &Enum_from_python_obj::convertible, - &Enum_from_python_obj::construct, - bpy::type_id()); - } - - template - inline void register_qclass_converter() { - using Converter = QClass_converter::QClass_converters; - bpy::converter::registry::insert(&Converter::QClass_from_PyQt, bpy::type_id()); - bpy::to_python_converter(); - bpy::to_python_converter(); - bpy::to_python_converter(); - } - - /** - * @brief Register a functor converter. - * - * @tparam Fn The function type to register. - * @tparam Wrappers... A list of wrapper (boost::python::pointer_wrapper or boost::reference_wrapper) - * indicating if parameters of the given (wrapped) type must be wrapped. - */ - template - inline void register_functor_converter() { - using Converter = Functor_converter; - bpy::converter::registry::push_back( - &Converter::convertible, - &Converter::construct, - bpy::type_id>()); - } - - - -} +} // namespace utils #endif diff --git a/src/runner-pybind11/error.h b/src/runner-pybind11/error.h index f73e4bd..ac8683f 100644 --- a/src/runner-pybind11/error.h +++ b/src/runner-pybind11/error.h @@ -36,10 +36,7 @@ namespace pyexcept { * python. If the message cannot be retrieved, `defaultErrorMessage()` * is used instead. */ - PythonError(pybind11::error_already_set const& ex) - : Exception(ex.what()) - { - } + PythonError(pybind11::error_already_set const& ex) : Exception(ex.what()) {} /** * @brief Create a new PythonError with the given message. @@ -75,8 +72,7 @@ namespace pyexcept { */ static QString defaultErrorMessage() { - return QObject::tr( - "An unknown exception was thrown in python code."); + return QObject::tr("An unknown exception was thrown in python code."); } }; diff --git a/src/runner-pybind11/gamefeatureswrappers.cpp b/src/runner-pybind11/gamefeatureswrappers.cpp index ea8a673..93e2b9e 100644 --- a/src/runner-pybind11/gamefeatureswrappers.cpp +++ b/src/runner-pybind11/gamefeatureswrappers.cpp @@ -17,26 +17,25 @@ bool BSAInvalidationWrapper::isInvalidationBSA(const QString& bsaName) { - return basicWrapperFunctionImplementation(this, "isInvalidationBSA", - bsaName); + return basicWrapperFunctionImplementation(this, "isInvalidationBSA", bsaName); } void BSAInvalidationWrapper::deactivate(MOBase::IProfile* profile) { - return basicWrapperFunctionImplementation( - this, "deactivate", boost::python::ptr(profile)); + return basicWrapperFunctionImplementation(this, "deactivate", + boost::python::ptr(profile)); } void BSAInvalidationWrapper::activate(MOBase::IProfile* profile) { - return basicWrapperFunctionImplementation( - this, "activate", boost::python::ptr(profile)); + return basicWrapperFunctionImplementation(this, "activate", + boost::python::ptr(profile)); } bool BSAInvalidationWrapper::prepareProfile(MOBase::IProfile* profile) { - return basicWrapperFunctionImplementation( - this, "prepareProfile", boost::python::ptr(profile)); + return basicWrapperFunctionImplementation(this, "prepareProfile", + boost::python::ptr(profile)); } /// end BSAInvalidation Wrapper ///////////////////////////// @@ -44,14 +43,13 @@ bool BSAInvalidationWrapper::prepareProfile(MOBase::IProfile* profile) QStringList DataArchivesWrapper::vanillaArchives() const { - return basicWrapperFunctionImplementation(this, - "vanillaArchives"); + return basicWrapperFunctionImplementation(this, "vanillaArchives"); } QStringList DataArchivesWrapper::archives(const MOBase::IProfile* profile) const { - return basicWrapperFunctionImplementation( - this, "archives", boost::python::ptr(profile)); + return basicWrapperFunctionImplementation(this, "archives", + boost::python::ptr(profile)); } void DataArchivesWrapper::addArchive(MOBase::IProfile* profile, int index, @@ -73,26 +71,24 @@ void DataArchivesWrapper::removeArchive(MOBase::IProfile* profile, void GamePluginsWrapper::writePluginLists(const MOBase::IPluginList* pluginList) { - return basicWrapperFunctionImplementation( - this, "writePluginLists", boost::python::ptr(pluginList)); + return basicWrapperFunctionImplementation(this, "writePluginLists", + boost::python::ptr(pluginList)); } void GamePluginsWrapper::readPluginLists(MOBase::IPluginList* pluginList) { - return basicWrapperFunctionImplementation( - this, "readPluginLists", boost::python::ptr(pluginList)); + return basicWrapperFunctionImplementation(this, "readPluginLists", + boost::python::ptr(pluginList)); } QStringList GamePluginsWrapper::getLoadOrder() { - return basicWrapperFunctionImplementation(this, - "getLoadOrder"); + return basicWrapperFunctionImplementation(this, "getLoadOrder"); } bool GamePluginsWrapper::lightPluginsAreSupported() { - return basicWrapperFunctionImplementation(this, - "lightPluginsAreSupported"); + return basicWrapperFunctionImplementation(this, "lightPluginsAreSupported"); } /// end GamePlugins Wrapper @@ -107,8 +103,8 @@ MappingType LocalSavegamesWrapper::mappings(const QDir& profileSaveDir) const bool LocalSavegamesWrapper::prepareProfile(MOBase::IProfile* profile) { - return basicWrapperFunctionImplementation( - this, "prepareProfile", boost::python::ptr(profile)); + return basicWrapperFunctionImplementation(this, "prepareProfile", + boost::python::ptr(profile)); } /// end LocalSavegames Wrapper @@ -118,38 +114,36 @@ bool LocalSavegamesWrapper::prepareProfile(MOBase::IProfile* profile) ModDataChecker::CheckReturn ModDataCheckerWrapper::dataLooksValid( std::shared_ptr fileTree) const { - return basicWrapperFunctionImplementation( - this, "dataLooksValid", fileTree); + return basicWrapperFunctionImplementation(this, "dataLooksValid", + fileTree); } std::shared_ptr ModDataCheckerWrapper::fix(std::shared_ptr fileTree) const { - return utils::clean_shared_ptr( - basicWrapperFunctionImplementationWithDefault< - std::shared_ptr>( - this, - [](auto&&... args) { - return nullptr; - }, - "fix", fileTree)); + return utils::clean_shared_ptr(basicWrapperFunctionImplementationWithDefault< + std::shared_ptr>( + this, + [](auto&&... args) { + return nullptr; + }, + "fix", fileTree)); } /// end ModDataChecker Wrapper ///////////////////////////// /// ModDataContent Wrapper -std::vector -ModDataContentWrapper::getAllContents() const +std::vector ModDataContentWrapper::getAllContents() const { - return basicWrapperFunctionImplementation>( - this, "getAllContents"); + return basicWrapperFunctionImplementation>(this, + "getAllContents"); } std::vector ModDataContentWrapper::getContentsFor( std::shared_ptr fileTree) const { - return basicWrapperFunctionImplementation>( - this, "getContentsFor", fileTree); + return basicWrapperFunctionImplementation>(this, "getContentsFor", + fileTree); } /// end ModDataContent Wrapper @@ -159,9 +153,8 @@ std::vector ModDataContentWrapper::getContentsFor( SaveGameInfoWrapper::MissingAssets SaveGameInfoWrapper::getMissingAssets(MOBase::ISaveGame const& save) const { - return basicWrapperFunctionImplementation< - SaveGameInfoWrapper::MissingAssets>(this, "getMissingAssets", - boost::ref(save)); + return basicWrapperFunctionImplementation( + this, "getMissingAssets", boost::ref(save)); } MOBase::ISaveGameInfoWidget* @@ -197,8 +190,7 @@ QString ScriptExtenderWrapper::loaderPath() const QString ScriptExtenderWrapper::savegameExtension() const { - return basicWrapperFunctionImplementation(this, - "savegameExtension"); + return basicWrapperFunctionImplementation(this, "savegameExtension"); } bool ScriptExtenderWrapper::isInstalled() const @@ -208,8 +200,7 @@ bool ScriptExtenderWrapper::isInstalled() const QString ScriptExtenderWrapper::getExtenderVersion() const { - return basicWrapperFunctionImplementation(this, - "getExtenderVersion"); + return basicWrapperFunctionImplementation(this, "getExtenderVersion"); } WORD ScriptExtenderWrapper::getArch() const @@ -223,14 +214,12 @@ WORD ScriptExtenderWrapper::getArch() const QStringList UnmanagedModsWrapper::mods(bool onlyOfficial) const { - return basicWrapperFunctionImplementation(this, "mods", - onlyOfficial); + return basicWrapperFunctionImplementation(this, "mods", onlyOfficial); } QString UnmanagedModsWrapper::displayName(const QString& modName) const { - return basicWrapperFunctionImplementation(this, "displayName", - modName); + return basicWrapperFunctionImplementation(this, "displayName", modName); } QFileInfo UnmanagedModsWrapper::referenceFile(const QString& modName) const @@ -241,8 +230,8 @@ QFileInfo UnmanagedModsWrapper::referenceFile(const QString& modName) const QStringList UnmanagedModsWrapper::secondaryFiles(const QString& modName) const { - return basicWrapperFunctionImplementation( - this, "secondaryFiles", modName); + return basicWrapperFunctionImplementation(this, "secondaryFiles", + modName); } /// end UnmanagedMods Wrapper ///////////////////////////// @@ -267,8 +256,7 @@ void insertGameFeature(std::map& map, } void game_features_map_from_python::construct( - PyObject* objPtr, - boost::python::converter::rvalue_from_python_stage1_data* data) + PyObject* objPtr, boost::python::converter::rvalue_from_python_stage1_data* data) { void* storage = ((boost::python::converter::rvalue_from_python_storage< std::map>*)data) @@ -307,16 +295,14 @@ void registerGameFeaturesPythonConverters() // Features require defs for all methods as Python can access C++ features bpy::class_("BSAInvalidation") .def("isInvalidationBSA", - bpy::pure_virtual(&BSAInvalidation::isInvalidationBSA), - bpy::arg("name")) + bpy::pure_virtual(&BSAInvalidation::isInvalidationBSA), bpy::arg("name")) .def("deactivate", bpy::pure_virtual(&BSAInvalidation::deactivate), bpy::arg("profile")) .def("activate", bpy::pure_virtual(&BSAInvalidation::activate), bpy::arg("profile")); bpy::class_("DataArchives") - .def("vanillaArchives", - bpy::pure_virtual(&DataArchives::vanillaArchives)) + .def("vanillaArchives", bpy::pure_virtual(&DataArchives::vanillaArchives)) .def("archives", bpy::pure_virtual(&DataArchives::archives), bpy::arg("profile")) .def("addArchive", bpy::pure_virtual(&DataArchives::addArchive), @@ -325,11 +311,9 @@ void registerGameFeaturesPythonConverters() (bpy::arg("profile"), "name")); bpy::class_("GamePlugins") - .def("writePluginLists", - bpy::pure_virtual(&GamePlugins::writePluginLists), + .def("writePluginLists", bpy::pure_virtual(&GamePlugins::writePluginLists), bpy::arg("plugin_list")) - .def("readPluginLists", - bpy::pure_virtual(&GamePlugins::readPluginLists), + .def("readPluginLists", bpy::pure_virtual(&GamePlugins::readPluginLists), bpy::arg("plugin_list")) .def("getLoadOrder", bpy::pure_virtual(&GamePlugins::getLoadOrder)) .def("lightPluginsAreSupported", @@ -338,13 +322,11 @@ void registerGameFeaturesPythonConverters() bpy::class_("LocalSavegames") .def("mappings", bpy::pure_virtual(&LocalSavegames::mappings), bpy::arg("profile_save_dir")) - .def("prepareProfile", - bpy::pure_virtual(&LocalSavegames::prepareProfile), + .def("prepareProfile", bpy::pure_virtual(&LocalSavegames::prepareProfile), bpy::arg("profile")); auto modDataCheckerClass = - bpy::class_( - "ModDataChecker"); + bpy::class_("ModDataChecker"); { bpy::scope scope = modDataCheckerClass; @@ -355,17 +337,14 @@ void registerGameFeaturesPythonConverters() .export_values(); modDataCheckerClass - .def("dataLooksValid", - bpy::pure_virtual(&ModDataChecker::dataLooksValid), + .def("dataLooksValid", bpy::pure_virtual(&ModDataChecker::dataLooksValid), bpy::arg("filetree")) - .def("fix", bpy::pure_virtual(&ModDataChecker::fix), - bpy::arg("filetree")); + .def("fix", bpy::pure_virtual(&ModDataChecker::fix), bpy::arg("filetree")); } { bpy::scope scope = - bpy::class_( - "ModDataContent") + bpy::class_("ModDataContent") .def("getAllContents", bpy::pure_virtual(&ModDataContent::getAllContents)) .def("getContentsFor", @@ -373,9 +352,9 @@ void registerGameFeaturesPythonConverters() bpy::arg("filetree")); bpy::class_( - "Content", bpy::init>( - (bpy::arg("id"), "name", "icon", - bpy::arg("filter_only") = false))) + "Content", + bpy::init>( + (bpy::arg("id"), "name", "icon", bpy::arg("filter_only") = false))) .add_property("id", &ModDataContent::Content::id) .add_property("name", &ModDataContent::Content::name) .add_property("icon", &ModDataContent::Content::icon) @@ -383,34 +362,29 @@ void registerGameFeaturesPythonConverters() } bpy::class_("SaveGameInfo") - .def("getMissingAssets", - bpy::pure_virtual(&SaveGameInfo::getMissingAssets), + .def("getMissingAssets", bpy::pure_virtual(&SaveGameInfo::getMissingAssets), bpy::arg("save")) - .def("getSaveGameWidget", - bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), - bpy::return_value_policy(), - bpy::arg("parent"), "[optional]"); + .def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), + bpy::return_value_policy(), bpy::arg("parent"), + "[optional]"); bpy::class_("ScriptExtender") .def("BinaryName", bpy::pure_virtual(&ScriptExtender::BinaryName)) .def("PluginPath", bpy::pure_virtual(&ScriptExtender::PluginPath)) .def("loaderName", bpy::pure_virtual(&ScriptExtender::loaderName)) .def("loaderPath", bpy::pure_virtual(&ScriptExtender::loaderPath)) - .def("savegameExtension", - bpy::pure_virtual(&ScriptExtender::savegameExtension)) + .def("savegameExtension", bpy::pure_virtual(&ScriptExtender::savegameExtension)) .def("isInstalled", bpy::pure_virtual(&ScriptExtender::isInstalled)) .def("getExtenderVersion", bpy::pure_virtual(&ScriptExtender::getExtenderVersion)) .def("getArch", bpy::pure_virtual(&ScriptExtender::getArch)); bpy::class_("UnmanagedMods") - .def("mods", bpy::pure_virtual(&UnmanagedMods::mods), - bpy::arg("official_only")) + .def("mods", bpy::pure_virtual(&UnmanagedMods::mods), bpy::arg("official_only")) .def("displayName", bpy::pure_virtual(&UnmanagedMods::displayName), bpy::arg("mod_name")) .def("referenceFile", bpy::pure_virtual(&UnmanagedMods::referenceFile), bpy::arg("mod_name")) - .def("secondaryFiles", - bpy::pure_virtual(&UnmanagedMods::secondaryFiles), + .def("secondaryFiles", bpy::pure_virtual(&UnmanagedMods::secondaryFiles), bpy::arg("mod_name")); } diff --git a/src/runner-pybind11/gamefeatureswrappers.h b/src/runner-pybind11/gamefeatureswrappers.h index 7155d8c..3cd94e5 100644 --- a/src/runner-pybind11/gamefeatureswrappers.h +++ b/src/runner-pybind11/gamefeatureswrappers.h @@ -14,145 +14,145 @@ #include // this might need turning off if Q_MOC_RUN is defined -#include #include +#include // This is a simple MPL list that contains all the game features in one place: -using MpGameFeaturesList = boost::mp11::mp_list< - BSAInvalidation, - DataArchives, - GamePlugins, - LocalSavegames, - ModDataChecker, - ModDataContent, - SaveGameInfo, - ScriptExtender, - UnmanagedMods ->; +using MpGameFeaturesList = + boost::mp11::mp_list; ///////////////////////////// /// Wrapper declarations -class BSAInvalidationWrapper : public BSAInvalidation, public boost::python::wrapper -{ +class BSAInvalidationWrapper : public BSAInvalidation, + public boost::python::wrapper { public: - static constexpr const char* className = "BSAInvalidationWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "BSAInvalidationWrapper"; + using boost::python::wrapper::get_override; - virtual bool isInvalidationBSA(const QString &bsaName) override; - virtual void deactivate(MOBase::IProfile *profile) override; - virtual void activate(MOBase::IProfile *profile) override; - virtual bool prepareProfile(MOBase::IProfile *profile) override; + virtual bool isInvalidationBSA(const QString& bsaName) override; + virtual void deactivate(MOBase::IProfile* profile) override; + virtual void activate(MOBase::IProfile* profile) override; + virtual bool prepareProfile(MOBase::IProfile* profile) override; }; -class DataArchivesWrapper : public DataArchives, public boost::python::wrapper -{ +class DataArchivesWrapper : public DataArchives, + public boost::python::wrapper { public: - static constexpr const char* className = "DataArchivesWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "DataArchivesWrapper"; + using boost::python::wrapper::get_override; - virtual QStringList vanillaArchives() const override; - virtual QStringList archives(const MOBase::IProfile *profile) const override; - virtual void addArchive(MOBase::IProfile *profile, int index, const QString &archiveName) override; - virtual void removeArchive(MOBase::IProfile *profile, const QString &archiveName) override; + virtual QStringList vanillaArchives() const override; + virtual QStringList archives(const MOBase::IProfile* profile) const override; + virtual void addArchive(MOBase::IProfile* profile, int index, + const QString& archiveName) override; + virtual void removeArchive(MOBase::IProfile* profile, + const QString& archiveName) override; }; -class GamePluginsWrapper : public GamePlugins, public boost::python::wrapper -{ +class GamePluginsWrapper : public GamePlugins, + public boost::python::wrapper { public: - static constexpr const char* className = "GamePluginsWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "GamePluginsWrapper"; + using boost::python::wrapper::get_override; - virtual void writePluginLists(const MOBase::IPluginList *pluginList) override; - virtual void readPluginLists(MOBase::IPluginList *pluginList) override; - virtual QStringList getLoadOrder() override; - virtual bool lightPluginsAreSupported() override; + virtual void writePluginLists(const MOBase::IPluginList* pluginList) override; + virtual void readPluginLists(MOBase::IPluginList* pluginList) override; + virtual QStringList getLoadOrder() override; + virtual bool lightPluginsAreSupported() override; }; -class LocalSavegamesWrapper : public LocalSavegames, public boost::python::wrapper -{ +class LocalSavegamesWrapper : public LocalSavegames, + public boost::python::wrapper { public: - static constexpr const char* className = "LocalSavegamesWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "LocalSavegamesWrapper"; + using boost::python::wrapper::get_override; - virtual MappingType mappings(const QDir &profileSaveDir) const override; - virtual bool prepareProfile(MOBase::IProfile *profile) override; + virtual MappingType mappings(const QDir& profileSaveDir) const override; + virtual bool prepareProfile(MOBase::IProfile* profile) override; }; -class ModDataCheckerWrapper : public ModDataChecker, public boost::python::wrapper -{ +class ModDataCheckerWrapper : public ModDataChecker, + public boost::python::wrapper { public: - static constexpr const char* className = "ModDataCheckerWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "ModDataCheckerWrapper"; + using boost::python::wrapper::get_override; - virtual CheckReturn dataLooksValid(std::shared_ptr fileTree) const override; - virtual std::shared_ptr fix(std::shared_ptr fileTree) const override; + virtual CheckReturn + dataLooksValid(std::shared_ptr fileTree) const override; + virtual std::shared_ptr + fix(std::shared_ptr fileTree) const override; }; -class ModDataContentWrapper : public ModDataContent, public boost::python::wrapper -{ +class ModDataContentWrapper : public ModDataContent, + public boost::python::wrapper { public: - static constexpr const char* className = "ModDataContentWrapper"; - using boost::python::wrapper::get_override; - - virtual std::vector getAllContents() const override; - virtual std::vector getContentsFor(std::shared_ptr fileTree) const override; + static constexpr const char* className = "ModDataContentWrapper"; + using boost::python::wrapper::get_override; + virtual std::vector getAllContents() const override; + virtual std::vector + getContentsFor(std::shared_ptr fileTree) const override; }; -class SaveGameInfoWrapper : public SaveGameInfo, public boost::python::wrapper -{ +class SaveGameInfoWrapper : public SaveGameInfo, + public boost::python::wrapper { public: - static constexpr const char* className = "SaveGameInfoWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "SaveGameInfoWrapper"; + using boost::python::wrapper::get_override; - virtual MissingAssets getMissingAssets(MOBase::ISaveGame const& save) const override; - virtual MOBase::ISaveGameInfoWidget *getSaveGameWidget(QWidget *parent = 0) const override; + virtual MissingAssets + getMissingAssets(MOBase::ISaveGame const& save) const override; + virtual MOBase::ISaveGameInfoWidget* + getSaveGameWidget(QWidget* parent = 0) const override; private: - // We need to keep the python objects alive: - mutable std::map m_SaveGames; - mutable boost::python::object m_SaveGameWidget; + // We need to keep the python objects alive: + mutable std::map m_SaveGames; + mutable boost::python::object m_SaveGameWidget; }; -class ScriptExtenderWrapper : public ScriptExtender, public boost::python::wrapper -{ +class ScriptExtenderWrapper : public ScriptExtender, + public boost::python::wrapper { public: - static constexpr const char* className = "ScriptExtenderWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "ScriptExtenderWrapper"; + using boost::python::wrapper::get_override; - virtual QString BinaryName() const override; - virtual QString PluginPath() const override; - virtual QString loaderName() const override; - virtual QString loaderPath() const override; - virtual QString savegameExtension() const override; - virtual bool isInstalled() const override; - virtual QString getExtenderVersion() const override; - virtual WORD getArch() const override; + virtual QString BinaryName() const override; + virtual QString PluginPath() const override; + virtual QString loaderName() const override; + virtual QString loaderPath() const override; + virtual QString savegameExtension() const override; + virtual bool isInstalled() const override; + virtual QString getExtenderVersion() const override; + virtual WORD getArch() const override; }; -class UnmanagedModsWrapper : public UnmanagedMods, public boost::python::wrapper -{ +class UnmanagedModsWrapper : public UnmanagedMods, + public boost::python::wrapper { public: - static constexpr const char* className = "UnmanagedModsWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "UnmanagedModsWrapper"; + using boost::python::wrapper::get_override; - virtual QStringList mods(bool onlyOfficial) const override; - virtual QString displayName(const QString &modName) const override; - virtual QFileInfo referenceFile(const QString &modName) const override; - virtual QStringList secondaryFiles(const QString &modName) const override; + virtual QStringList mods(bool onlyOfficial) const override; + virtual QString displayName(const QString& modName) const override; + virtual QFileInfo referenceFile(const QString& modName) const override; + virtual QStringList secondaryFiles(const QString& modName) const override; }; /// end Wrapper declarations ///////////////////////////// -struct game_features_map_from_python -{ - game_features_map_from_python(); - static void *convertible(PyObject *objPtr); - static void construct(PyObject *objPtr, boost::python::converter::rvalue_from_python_stage1_data *data); +struct game_features_map_from_python { + game_features_map_from_python(); + static void* convertible(PyObject* objPtr); + static void + construct(PyObject* objPtr, + boost::python::converter::rvalue_from_python_stage1_data* data); }; void registerGameFeaturesPythonConverters(); -#endif // GAMEFEATURESWRAPPERS_H +#endif // GAMEFEATURESWRAPPERS_H diff --git a/src/runner-pybind11/gilock.cpp b/src/runner-pybind11/gilock.cpp index 8543935..7cdca42 100644 --- a/src/runner-pybind11/gilock.cpp +++ b/src/runner-pybind11/gilock.cpp @@ -2,11 +2,11 @@ GILock::GILock() { - m_State = PyGILState_Ensure(); + m_State = PyGILState_Ensure(); } GILock::~GILock() { - PyErr_Clear(); - PyGILState_Release(m_State); + PyErr_Clear(); + PyGILState_Release(m_State); } diff --git a/src/runner-pybind11/gilock.h b/src/runner-pybind11/gilock.h index c2425a6..61fd6ee 100644 --- a/src/runner-pybind11/gilock.h +++ b/src/runner-pybind11/gilock.h @@ -1,18 +1,17 @@ #ifndef GILOCK_H #define GILOCK_H - #ifndef Q_MOC_RUN #include -#endif // Q_MOC_RUN +#endif // Q_MOC_RUN class GILock { public: - GILock(); - ~GILock(); + GILock(); + ~GILock(); + private: - PyGILState_STATE m_State; + PyGILState_STATE m_State; }; - -#endif // GILOCK_H +#endif // GILOCK_H diff --git a/src/runner-pybind11/proxypluginwrappers.cpp b/src/runner-pybind11/proxypluginwrappers.cpp index bb64065..de0c938 100644 --- a/src/runner-pybind11/proxypluginwrappers.cpp +++ b/src/runner-pybind11/proxypluginwrappers.cpp @@ -4,136 +4,183 @@ #include #include -#include "shared_ptr_converter.h" #include "pythonwrapperutilities.h" -#include "uibasewrappers.h" +#include "shared_ptr_converter.h" -#include #include +#include -namespace boost -{ - // See bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2852624 +namespace boost { + // See bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2852624 #if (_MSC_VER == 1900) - template<> const volatile MOBase::IOrganizer* get_pointer(const volatile MOBase::IOrganizer* p) { return p; } - template<> const volatile MOBase::IModInterface* get_pointer(const volatile MOBase::IModInterface* p) { return p; } - template<> const volatile MOBase::IPluginGame* get_pointer(const volatile MOBase::IPluginGame* p) { return p; } - template<> const volatile MOBase::IProfile* get_pointer(const volatile MOBase::IProfile* p) { return p; } - template<> const volatile MOBase::IModList* get_pointer(const volatile MOBase::IModList* p) { return p; } - template<> const volatile MOBase::IPluginList* get_pointer(const volatile MOBase::IPluginList* p) { return p; } - template<> const volatile MOBase::IDownloadManager* get_pointer(const volatile MOBase::IDownloadManager* p) { return p; } - template<> const volatile MOBase::IModRepositoryBridge* get_pointer(const volatile MOBase::IModRepositoryBridge* p) { return p; } + template <> + const volatile MOBase::IOrganizer* get_pointer(const volatile MOBase::IOrganizer* p) + { + return p; + } + template <> + const volatile MOBase::IModInterface* + get_pointer(const volatile MOBase::IModInterface* p) + { + return p; + } + template <> + const volatile MOBase::IPluginGame* + get_pointer(const volatile MOBase::IPluginGame* p) + { + return p; + } + template <> + const volatile MOBase::IProfile* get_pointer(const volatile MOBase::IProfile* p) + { + return p; + } + template <> + const volatile MOBase::IModList* get_pointer(const volatile MOBase::IModList* p) + { + return p; + } + template <> + const volatile MOBase::IPluginList* + get_pointer(const volatile MOBase::IPluginList* p) + { + return p; + } + template <> + const volatile MOBase::IDownloadManager* + get_pointer(const volatile MOBase::IDownloadManager* p) + { + return p; + } + template <> + const volatile MOBase::IModRepositoryBridge* + get_pointer(const volatile MOBase::IModRepositoryBridge* p) + { + return p; + } #endif -} +} // namespace boost using namespace MOBase; -// See COMMON_I_PLUGIN_WRAPPER_DECLARATIONS__IMPL in proxypluginwrappers.h for explanation on -// the "include_requirements". -#define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS_IMPL(class_name, include_requirements) \ -bool class_name::init(MOBase::IOrganizer *moInfo) \ -{ \ - return basicWrapperFunctionImplementation(this, "init", boost::python::ptr(moInfo)); \ -} \ - \ -QString class_name::name() const \ -{ \ - return basicWrapperFunctionImplementation(this, "name"); \ -} \ - \ -QString class_name::localizedName() const \ -{ \ - return basicWrapperFunctionImplementationWithDefault(this, &class_name::localizedName_Default, "localizedName"); \ -} \ - \ -QString class_name::master() const \ -{ \ - return basicWrapperFunctionImplementationWithDefault(this, &class_name::master_Default, "master"); \ -} \ - \ -QString class_name::author() const \ -{ \ - return basicWrapperFunctionImplementation(this, "author"); \ -} \ - \ -QString class_name::description() const \ -{ \ - return basicWrapperFunctionImplementation(this, "description"); \ -} \ - \ -MOBase::VersionInfo class_name::version() const \ -{ \ - return basicWrapperFunctionImplementation(this, "version"); \ -} \ - \ -QList class_name::settings() const \ -{ \ - return basicWrapperFunctionImplementation>(this, "settings"); \ -} \ -QString class_name::localizedName_Default() const { return IPlugin::localizedName(); } \ -QString class_name::master_Default() const { return IPlugin::master(); } \ -BOOST_PP_EXPR_IF(include_requirements, \ - std::vector> class_name::requirements() const { \ - return basicWrapperFunctionImplementationWithDefault>>( \ - this, &class_name::requirements_Default, "requirements"); \ - } \ - std::vector> class_name::requirements_Default() const { return IPlugin::requirements(); } \ - bool class_name::enabledByDefault() const \ - { \ - return basicWrapperFunctionImplementationWithDefault(this, &class_name::enabledByDefault_Default, "enabledByDefault"); \ - } \ - bool class_name::enabledByDefault_Default() const { return IPlugin::enabledByDefault(); }) +// See COMMON_I_PLUGIN_WRAPPER_DECLARATIONS__IMPL in proxypluginwrappers.h for +// explanation on the "include_requirements". +#define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS_IMPL(class_name, include_requirements) \ + bool class_name::init(MOBase::IOrganizer* moInfo) \ + { \ + return basicWrapperFunctionImplementation(this, "init", \ + boost::python::ptr(moInfo)); \ + } \ + \ + QString class_name::name() const \ + { \ + return basicWrapperFunctionImplementation(this, "name"); \ + } \ + \ + QString class_name::localizedName() const \ + { \ + return basicWrapperFunctionImplementationWithDefault( \ + this, &class_name::localizedName_Default, "localizedName"); \ + } \ + \ + QString class_name::master() const \ + { \ + return basicWrapperFunctionImplementationWithDefault( \ + this, &class_name::master_Default, "master"); \ + } \ + \ + QString class_name::author() const \ + { \ + return basicWrapperFunctionImplementation(this, "author"); \ + } \ + \ + QString class_name::description() const \ + { \ + return basicWrapperFunctionImplementation(this, "description"); \ + } \ + \ + MOBase::VersionInfo class_name::version() const \ + { \ + return basicWrapperFunctionImplementation(this, \ + "version"); \ + } \ + \ + QList class_name::settings() const \ + { \ + return basicWrapperFunctionImplementation>( \ + this, "settings"); \ + } \ + QString class_name::localizedName_Default() const \ + { \ + return IPlugin::localizedName(); \ + } \ + QString class_name::master_Default() const { return IPlugin::master(); } \ + BOOST_PP_EXPR_IF( \ + include_requirements, \ + std::vector> \ + class_name::requirements() const { \ + return basicWrapperFunctionImplementationWithDefault< \ + std::vector>>( \ + this, &class_name::requirements_Default, "requirements"); \ + } std::vector> \ + class_name::requirements_Default() const { \ + return IPlugin::requirements(); \ + } bool class_name::enabledByDefault() const { \ + return basicWrapperFunctionImplementationWithDefault( \ + this, &class_name::enabledByDefault_Default, \ + "enabledByDefault"); \ + } bool class_name::enabledByDefault_Default() \ + const { return IPlugin::enabledByDefault(); }) -#define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(class_name) COMMON_I_PLUGIN_WRAPPER_DEFINITIONS_IMPL(class_name, 1) +#define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(class_name) \ + COMMON_I_PLUGIN_WRAPPER_DEFINITIONS_IMPL(class_name, 1) /// end COMMON_I_PLUGIN_WRAPPER_DEFINITIONS ///////////////////////////// /// IPlugin Wrapper - COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginWrapper) /// end IPlugin Wrapper ///////////////////////////////////// /// IPluginDiagnose Wrapper - COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginDiagnoseWrapper) std::vector IPluginDiagnoseWrapper::activeProblems() const { - return basicWrapperFunctionImplementation>(this, "activeProblems"); + return basicWrapperFunctionImplementation>( + this, "activeProblems"); } QString IPluginDiagnoseWrapper::shortDescription(unsigned int key) const { - return basicWrapperFunctionImplementation(this, "shortDescription", key); + return basicWrapperFunctionImplementation(this, "shortDescription", key); } QString IPluginDiagnoseWrapper::fullDescription(unsigned int key) const { - return basicWrapperFunctionImplementation(this, "fullDescription", key); + return basicWrapperFunctionImplementation(this, "fullDescription", key); } bool IPluginDiagnoseWrapper::hasGuidedFix(unsigned int key) const { - return basicWrapperFunctionImplementation(this, "hasGuidedFix", key); + return basicWrapperFunctionImplementation(this, "hasGuidedFix", key); } void IPluginDiagnoseWrapper::startGuidedFix(unsigned int key) const { - basicWrapperFunctionImplementation(this, "startGuidedFix", key); + basicWrapperFunctionImplementation(this, "startGuidedFix", key); } /// end IPluginDiagnose Wrapper ///////////////////////////////////// /// IPluginFileMapper Wrapper - COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginFileMapperWrapper) MappingType IPluginFileMapperWrapper::mappings() const { - return basicWrapperFunctionImplementation(this, "mappings"); + return basicWrapperFunctionImplementation(this, "mappings"); } /// end IPluginFileMapper Wrapper ///////////////////////////////////// @@ -141,184 +188,215 @@ MappingType IPluginFileMapperWrapper::mappings() const void IPluginGameWrapper::detectGame() { - return basicWrapperFunctionImplementation(this, "detectGame"); + return basicWrapperFunctionImplementation(this, "detectGame"); } QString IPluginGameWrapper::gameName() const { - return basicWrapperFunctionImplementation(this, "gameName"); + return basicWrapperFunctionImplementation(this, "gameName"); } -void IPluginGameWrapper::initializeProfile(const QDir & directory, ProfileSettings settings) const +void IPluginGameWrapper::initializeProfile(const QDir& directory, + ProfileSettings settings) const { - basicWrapperFunctionImplementation(this, "initializeProfile", directory, settings); + basicWrapperFunctionImplementation(this, "initializeProfile", directory, + settings); } -std::vector> IPluginGameWrapper::listSaves(QDir folder) const +std::vector> +IPluginGameWrapper::listSaves(QDir folder) const { - // Why do I not need to hold python references here? Is it because those are wrapped - // in shared_ptr? - return basicWrapperFunctionImplementation>>(this, "listSaves", folder); + // Why do I not need to hold python references here? Is it because those are wrapped + // in shared_ptr? + return basicWrapperFunctionImplementation< + std::vector>>(this, "listSaves", + folder); } bool IPluginGameWrapper::isInstalled() const { - return basicWrapperFunctionImplementation(this, "isInstalled"); + return basicWrapperFunctionImplementation(this, "isInstalled"); } QIcon IPluginGameWrapper::gameIcon() const { - return basicWrapperFunctionImplementation(this, "gameIcon"); + return basicWrapperFunctionImplementation(this, "gameIcon"); } QDir IPluginGameWrapper::gameDirectory() const { - return basicWrapperFunctionImplementation(this, "gameDirectory"); + return basicWrapperFunctionImplementation(this, "gameDirectory"); } QDir IPluginGameWrapper::dataDirectory() const { - return basicWrapperFunctionImplementation(this, "dataDirectory"); + return basicWrapperFunctionImplementation(this, "dataDirectory"); } -void IPluginGameWrapper::setGamePath(const QString & path) +void IPluginGameWrapper::setGamePath(const QString& path) { - basicWrapperFunctionImplementation(this, "setGamePath", path); + basicWrapperFunctionImplementation(this, "setGamePath", path); } QDir IPluginGameWrapper::documentsDirectory() const { - return basicWrapperFunctionImplementation(this, "documentsDirectory"); + return basicWrapperFunctionImplementation(this, "documentsDirectory"); } QDir IPluginGameWrapper::savesDirectory() const { - return basicWrapperFunctionImplementation(this, "savesDirectory"); + return basicWrapperFunctionImplementation(this, "savesDirectory"); } QList IPluginGameWrapper::executables() const { - return basicWrapperFunctionImplementation>(this, "executables"); + return basicWrapperFunctionImplementation>( + this, "executables"); } -QList IPluginGameWrapper::executableForcedLoads() const +QList +IPluginGameWrapper::executableForcedLoads() const { - return basicWrapperFunctionImplementation>(this, "executableForcedLoads"); + return basicWrapperFunctionImplementation< + QList>(this, "executableForcedLoads"); } QString IPluginGameWrapper::steamAPPId() const { - return basicWrapperFunctionImplementation(this, "steamAPPId"); + return basicWrapperFunctionImplementation(this, "steamAPPId"); } QStringList IPluginGameWrapper::primaryPlugins() const { - return basicWrapperFunctionImplementation(this, "primaryPlugins"); + return basicWrapperFunctionImplementation(this, "primaryPlugins"); } QStringList IPluginGameWrapper::gameVariants() const { - return basicWrapperFunctionImplementation(this, "gameVariants"); + return basicWrapperFunctionImplementation(this, "gameVariants"); } -void IPluginGameWrapper::setGameVariant(const QString & variant) +void IPluginGameWrapper::setGameVariant(const QString& variant) { - basicWrapperFunctionImplementation(this, "setGameVariant", variant); + basicWrapperFunctionImplementation(this, "setGameVariant", variant); } QString IPluginGameWrapper::binaryName() const { - return basicWrapperFunctionImplementation(this, "binaryName"); + return basicWrapperFunctionImplementation(this, "binaryName"); } QString IPluginGameWrapper::gameShortName() const { - return basicWrapperFunctionImplementation(this, "gameShortName"); + return basicWrapperFunctionImplementation(this, "gameShortName"); } QStringList IPluginGameWrapper::primarySources() const { - return basicWrapperFunctionImplementation(this, "primarySources"); + return basicWrapperFunctionImplementation(this, "primarySources"); } QStringList IPluginGameWrapper::validShortNames() const { - return basicWrapperFunctionImplementation(this, "validShortNames"); + return basicWrapperFunctionImplementation(this, "validShortNames"); } QString IPluginGameWrapper::gameNexusName() const { - return basicWrapperFunctionImplementation(this, "gameNexusName"); + return basicWrapperFunctionImplementation(this, "gameNexusName"); } QStringList IPluginGameWrapper::iniFiles() const { - return basicWrapperFunctionImplementation(this, "iniFiles"); + return basicWrapperFunctionImplementation(this, "iniFiles"); } QStringList IPluginGameWrapper::DLCPlugins() const { - return basicWrapperFunctionImplementation(this, "DLCPlugins"); + return basicWrapperFunctionImplementation(this, "DLCPlugins"); } QStringList IPluginGameWrapper::CCPlugins() const { - return basicWrapperFunctionImplementation(this, "CCPlugins"); + return basicWrapperFunctionImplementation(this, "CCPlugins"); } IPluginGame::LoadOrderMechanism IPluginGameWrapper::loadOrderMechanism() const { - return basicWrapperFunctionImplementation(this, "loadOrderMechanism"); + return basicWrapperFunctionImplementation( + this, "loadOrderMechanism"); } IPluginGame::SortMechanism IPluginGameWrapper::sortMechanism() const { - return basicWrapperFunctionImplementation(this, "sortMechanism"); + return basicWrapperFunctionImplementation( + this, "sortMechanism"); } int IPluginGameWrapper::nexusModOrganizerID() const { - return basicWrapperFunctionImplementation(this, "nexusModOrganizerID"); + return basicWrapperFunctionImplementation(this, "nexusModOrganizerID"); } int IPluginGameWrapper::nexusGameID() const { - return basicWrapperFunctionImplementation(this, "nexusGameID"); + return basicWrapperFunctionImplementation(this, "nexusGameID"); } -bool IPluginGameWrapper::looksValid(QDir const & dir) const +bool IPluginGameWrapper::looksValid(QDir const& dir) const { - return basicWrapperFunctionImplementation(this, "looksValid", dir); + return basicWrapperFunctionImplementation(this, "looksValid", dir); } QString IPluginGameWrapper::gameVersion() const { - return basicWrapperFunctionImplementation(this, "gameVersion"); + return basicWrapperFunctionImplementation(this, "gameVersion"); } QString IPluginGameWrapper::getLauncherName() const { - return basicWrapperFunctionImplementation(this, "getLauncherName"); + return basicWrapperFunctionImplementation(this, "getLauncherName"); } COMMON_I_PLUGIN_WRAPPER_DEFINITIONS_IMPL(IPluginGameWrapper, 0) std::map IPluginGameWrapper::featureList() const { - return basicWrapperFunctionImplementation>(this, "_featureList"); + return basicWrapperFunctionImplementation>( + this, "_featureList"); } /// end IPluginGame Wrapper ///////////////////////////////////// /// IPluginInstaller macro -#define COMMON_I_PLUGIN_INSTALLER_WRAPPER_DEFINITIONS(class_name) \ -unsigned int class_name::priority() const { return basicWrapperFunctionImplementation(this, "priority"); } \ -bool class_name::isManualInstaller() const { return basicWrapperFunctionImplementation(this, "isManualInstaller"); } \ -void class_name::onInstallationStart(QString const& archive, bool reinstallation, MOBase::IModInterface* currentMod) { \ - basicWrapperFunctionImplementationWithDefault(this, &class_name::onInstallationStart_Default, "onInstallationStart", archive, reinstallation, boost::python::ptr(currentMod)); } \ -void class_name::onInstallationEnd(EInstallResult result, MOBase::IModInterface* newMod) { \ - basicWrapperFunctionImplementationWithDefault(this, &class_name::onInstallationEnd_Default, "onInstallationEnd", result, boost::python::ptr(newMod)); } \ -bool class_name::isArchiveSupported(std::shared_ptr tree) const { return basicWrapperFunctionImplementation(this, "isArchiveSupported", tree); } +#define COMMON_I_PLUGIN_INSTALLER_WRAPPER_DEFINITIONS(class_name) \ + unsigned int class_name::priority() const \ + { \ + return basicWrapperFunctionImplementation(this, "priority"); \ + } \ + bool class_name::isManualInstaller() const \ + { \ + return basicWrapperFunctionImplementation(this, "isManualInstaller"); \ + } \ + void class_name::onInstallationStart(QString const& archive, bool reinstallation, \ + MOBase::IModInterface* currentMod) \ + { \ + basicWrapperFunctionImplementationWithDefault( \ + this, &class_name::onInstallationStart_Default, "onInstallationStart", \ + archive, reinstallation, boost::python::ptr(currentMod)); \ + } \ + void class_name::onInstallationEnd(EInstallResult result, \ + MOBase::IModInterface* newMod) \ + { \ + basicWrapperFunctionImplementationWithDefault( \ + this, &class_name::onInstallationEnd_Default, "onInstallationEnd", result, \ + boost::python::ptr(newMod)); \ + } \ + bool class_name::isArchiveSupported(std::shared_ptr tree) const \ + { \ + return basicWrapperFunctionImplementation(this, "isArchiveSupported", \ + tree); \ + } /// end IPluginInstaller macro ///////////////////////////////////// @@ -327,37 +405,44 @@ bool class_name::isArchiveSupported(std::shared_ptr tree) const COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginInstallerSimpleWrapper) COMMON_I_PLUGIN_INSTALLER_WRAPPER_DEFINITIONS(IPluginInstallerSimpleWrapper) -IPluginInstaller::EInstallResult IPluginInstallerSimpleWrapper::install( - GuessedValue& modName, std::shared_ptr& tree, - QString& version, int& nexusID) +IPluginInstaller::EInstallResult +IPluginInstallerSimpleWrapper::install(GuessedValue& modName, + std::shared_ptr& tree, + QString& version, int& nexusID) { - namespace bpy = boost::python; + namespace bpy = boost::python; - using return_type = std::variant< - IPluginInstaller::EInstallResult, - std::shared_ptr, - std::tuple, QString, int>>; - auto ret = basicWrapperFunctionImplementation(this, "install", boost::ref(modName), tree, version, nexusID); + using return_type = + std::variant, + std::tuple, QString, int>>; + auto ret = basicWrapperFunctionImplementation( + this, "install", boost::ref(modName), tree, version, nexusID); - auto result = std::visit([&](auto const& t) { - using type = std::decay_t; - if constexpr (std::is_same_v) { - return t; - } - else if constexpr (std::is_same_v>) { - tree = t; - return IPluginInstaller::RESULT_SUCCESS; - } - else if constexpr (std::is_same_v, QString, int>>) { - tree = std::get<1>(t); - version = std::get<2>(t); - nexusID = std::get<3>(t); - return std::get<0>(t); - } - }, ret); + auto result = std::visit( + [&](auto const& t) { + using type = std::decay_t; + if constexpr (std::is_same_v) { + return t; + } + else if constexpr (std::is_same_v>) { + tree = t; + return IPluginInstaller::RESULT_SUCCESS; + } + else if constexpr (std::is_same_v< + type, std::tuple, QString, + int>>) { + tree = std::get<1>(t); + version = std::get<2>(t); + nexusID = std::get<3>(t); + return std::get<0>(t); + } + }, + ret); - tree = utils::clean_shared_ptr(tree); - return result; + tree = utils::clean_shared_ptr(tree); + return result; } /// end IPluginInstallerSimple Wrapper @@ -366,108 +451,118 @@ IPluginInstaller::EInstallResult IPluginInstallerSimpleWrapper::install( COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginInstallerCustomWrapper) COMMON_I_PLUGIN_INSTALLER_WRAPPER_DEFINITIONS(IPluginInstallerCustomWrapper) -bool IPluginInstallerCustomWrapper::isArchiveSupported(const QString &archiveName) const +bool IPluginInstallerCustomWrapper::isArchiveSupported(const QString& archiveName) const { - return basicWrapperFunctionImplementation(this, "isArchiveSupported", archiveName); + return basicWrapperFunctionImplementation(this, "isArchiveSupported", + archiveName); } std::set IPluginInstallerCustomWrapper::supportedExtensions() const { - return basicWrapperFunctionImplementation>(this, "supportedExtensions"); + return basicWrapperFunctionImplementation>(this, + "supportedExtensions"); } -IPluginInstaller::EInstallResult IPluginInstallerCustomWrapper::install( - GuessedValue &modName, QString gameName, const QString &archiveName, const QString &version, int modID) +IPluginInstaller::EInstallResult +IPluginInstallerCustomWrapper::install(GuessedValue& modName, QString gameName, + const QString& archiveName, + const QString& version, int modID) { - // Note: This requires far more less trouble than the "Simple" installer version since 1) there is no tree - // and 2) there version and modId cannot be modified: - return basicWrapperFunctionImplementation( - this, "install", boost::ref(modName), gameName, archiveName, version, modID); + // Note: This requires far more less trouble than the "Simple" installer version + // since 1) there is no tree and 2) there version and modId cannot be modified: + return basicWrapperFunctionImplementation( + this, "install", boost::ref(modName), gameName, archiveName, version, modID); } /// end IPluginInstallerCustom Wrapper ///////////////////////////// /// IPluginModPage Wrapper - COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginModPageWrapper) QString IPluginModPageWrapper::displayName() const { - return basicWrapperFunctionImplementation(this, "displayName"); + return basicWrapperFunctionImplementation(this, "displayName"); } QIcon IPluginModPageWrapper::icon() const { - return basicWrapperFunctionImplementation(this, "icon"); + return basicWrapperFunctionImplementation(this, "icon"); } QUrl IPluginModPageWrapper::pageURL() const { - return basicWrapperFunctionImplementation(this, "pageURL"); + return basicWrapperFunctionImplementation(this, "pageURL"); } bool IPluginModPageWrapper::useIntegratedBrowser() const { - return basicWrapperFunctionImplementation(this, "useIntegratedBrowser"); + return basicWrapperFunctionImplementation(this, "useIntegratedBrowser"); } -bool IPluginModPageWrapper::handlesDownload(const QUrl & pageURL, const QUrl & downloadURL, MOBase::ModRepositoryFileInfo & fileInfo) const +bool IPluginModPageWrapper::handlesDownload( + const QUrl& pageURL, const QUrl& downloadURL, + MOBase::ModRepositoryFileInfo& fileInfo) const { - return basicWrapperFunctionImplementation(this, "handlesDownload", pageURL, downloadURL, fileInfo); + return basicWrapperFunctionImplementation(this, "handlesDownload", pageURL, + downloadURL, fileInfo); } -void IPluginModPageWrapper::setParentWidget(QWidget * widget) +void IPluginModPageWrapper::setParentWidget(QWidget* widget) { - basicWrapperFunctionImplementationWithDefault(this, &IPluginModPageWrapper::setParentWidget_Default, "setParentWidget", widget); + basicWrapperFunctionImplementationWithDefault( + this, &IPluginModPageWrapper::setParentWidget_Default, "setParentWidget", + widget); } /// end IPluginModPage Wrapper ///////////////////////////// /// IPluginPreview Wrapper - COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginPreviewWrapper) std::set IPluginPreviewWrapper::supportedExtensions() const { - return basicWrapperFunctionImplementation>(this, "supportedExtensions"); + return basicWrapperFunctionImplementation>(this, + "supportedExtensions"); } -QWidget *IPluginPreviewWrapper::genFilePreview(const QString &fileName, const QSize &maxSize) const +QWidget* IPluginPreviewWrapper::genFilePreview(const QString& fileName, + const QSize& maxSize) const { - // We need responsibility for deleting the QWidget to be transferred to C++: - return wrapperFunctionImplementationWithApiTransfer(this, "genFilePreview", fileName, maxSize); + // We need responsibility for deleting the QWidget to be transferred to C++: + return wrapperFunctionImplementationWithApiTransfer( + this, "genFilePreview", fileName, maxSize); } /// end IPluginPreview Wrapper ///////////////////////////// /// IPluginTool Wrapper - COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginToolWrapper) QString IPluginToolWrapper::displayName() const { - return basicWrapperFunctionImplementation(this, "displayName"); + return basicWrapperFunctionImplementation(this, "displayName"); } QString IPluginToolWrapper::tooltip() const { - return basicWrapperFunctionImplementation(this, "tooltip"); + return basicWrapperFunctionImplementation(this, "tooltip"); } QIcon IPluginToolWrapper::icon() const { - return basicWrapperFunctionImplementation(this, "icon"); + return basicWrapperFunctionImplementation(this, "icon"); } -void IPluginToolWrapper::setParentWidget(QWidget *parent) +void IPluginToolWrapper::setParentWidget(QWidget* parent) { - basicWrapperFunctionImplementationWithDefault(this, &IPluginToolWrapper::setParentWidget_Default, "setParentWidget", parent); + basicWrapperFunctionImplementationWithDefault( + this, &IPluginToolWrapper::setParentWidget_Default, "setParentWidget", parent); } void IPluginToolWrapper::display() const { - basicWrapperFunctionImplementation(this, "display"); + basicWrapperFunctionImplementation(this, "display"); } /// end IPluginTool Wrapper diff --git a/src/runner-pybind11/proxypluginwrappers.h b/src/runner-pybind11/proxypluginwrappers.h index 524adde..dd582aa 100644 --- a/src/runner-pybind11/proxypluginwrappers.h +++ b/src/runner-pybind11/proxypluginwrappers.h @@ -1,268 +1,299 @@ #ifndef PROXYPLUGINWRAPPERS_H #define PROXYPLUGINWRAPPERS_H - #include #include #include #include -#include #include +#include #include #include #include #ifndef Q_MOC_RUN -#include #include +#include #endif -// The wrapper for IPluginGame cannot override requirements or enabledByDefault since they're final, -// so we need to be able to exclude the declarations. -#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(include_requirements) \ -public: \ -virtual bool init(MOBase::IOrganizer *moInfo) override; \ -virtual QString name() const override; \ -virtual QString localizedName() const override; \ -virtual QString master() const override; \ -virtual QString author() const override; \ -virtual QString description() const override; \ -virtual MOBase::VersionInfo version() const override; \ -virtual QList settings() const override; \ -QString localizedName_Default() const; \ -QString master_Default() const; \ -BOOST_PP_EXPR_IF(include_requirements, \ - virtual std::vector> requirements() const override; \ - std::vector> requirements_Default() const; \ - virtual bool enabledByDefault() const override; \ - bool enabledByDefault_Default() const;) +// The wrapper for IPluginGame cannot override requirements or enabledByDefault since +// they're final, so we need to be able to exclude the declarations. +#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(include_requirements) \ +public: \ + virtual bool init(MOBase::IOrganizer* moInfo) override; \ + virtual QString name() const override; \ + virtual QString localizedName() const override; \ + virtual QString master() const override; \ + virtual QString author() const override; \ + virtual QString description() const override; \ + virtual MOBase::VersionInfo version() const override; \ + virtual QList settings() const override; \ + QString localizedName_Default() const; \ + QString master_Default() const; \ + BOOST_PP_EXPR_IF( \ + include_requirements, \ + virtual std::vector> \ + requirements() const override; \ + std::vector> \ + requirements_Default() const; \ + virtual bool enabledByDefault() const override; \ + bool enabledByDefault_Default() const;) -#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(1) +#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS \ + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(1) -// Even though the base interface is not a QObject, this has to be because we have no way to pass Mod Organizer a plugin that implements multiple interfaces. -// QObject must be the first base class because moc assumes the first base class is a QObject -class IPluginWrapper : public QObject, public MOBase::IPlugin, public boost::python::wrapper -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin) +// Even though the base interface is not a QObject, this has to be because we have no +// way to pass Mod Organizer a plugin that implements multiple interfaces. QObject must +// be the first base class because moc assumes the first base class is a QObject +class IPluginWrapper : public QObject, + public MOBase::IPlugin, + public boost::python::wrapper { + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin) - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: - static constexpr const char* className = "IPluginWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "IPluginWrapper"; + using boost::python::wrapper::get_override; }; - -// Even though the base interface is not an IPlugin or QObject, this has to be because we have no way to pass Mod Organizer a plugin that implements multiple interfaces. -// QObject must be the first base class because moc assumes the first base class is a QObject -class IPluginDiagnoseWrapper : public QObject, public MOBase::IPluginDiagnose, public MOBase::IPlugin, public boost::python::wrapper -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginDiagnose) +// Even though the base interface is not an IPlugin or QObject, this has to be because +// we have no way to pass Mod Organizer a plugin that implements multiple interfaces. +// QObject must be the first base class because moc assumes the first base class is a +// QObject +class IPluginDiagnoseWrapper : public QObject, + public MOBase::IPluginDiagnose, + public MOBase::IPlugin, + public boost::python::wrapper { + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginDiagnose) public: - static constexpr const char* className = "IPluginDiagnoseWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "IPluginDiagnoseWrapper"; + using boost::python::wrapper::get_override; - // Bring in public scope: - using IPluginDiagnose::invalidate; + // Bring in public scope: + using IPluginDiagnose::invalidate; - virtual std::vector activeProblems() const override; - virtual QString shortDescription(unsigned int key) const override; - virtual QString fullDescription(unsigned int key) const override; - virtual bool hasGuidedFix(unsigned int key) const override; - virtual void startGuidedFix(unsigned int key) const override; + virtual std::vector activeProblems() const override; + virtual QString shortDescription(unsigned int key) const override; + virtual QString fullDescription(unsigned int key) const override; + virtual bool hasGuidedFix(unsigned int key) const override; + virtual void startGuidedFix(unsigned int key) const override; - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS }; - -// Even though the base interface is not an IPlugin or QObject, this has to be because we have no way to pass Mod Organizer a plugin that implements multiple interfaces. -// QObject must be the first base class because moc assumes the first base class is a QObject -class IPluginFileMapperWrapper : public QObject, public MOBase::IPluginFileMapper, public MOBase::IPlugin, public boost::python::wrapper -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginFileMapper) +// Even though the base interface is not an IPlugin or QObject, this has to be because +// we have no way to pass Mod Organizer a plugin that implements multiple interfaces. +// QObject must be the first base class because moc assumes the first base class is a +// QObject +class IPluginFileMapperWrapper + : public QObject, + public MOBase::IPluginFileMapper, + public MOBase::IPlugin, + public boost::python::wrapper { + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginFileMapper) public: - static constexpr const char* className = "IPluginFileMapperWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "IPluginFileMapperWrapper"; + using boost::python::wrapper::get_override; - virtual MappingType mappings() const override; + virtual MappingType mappings() const override; - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS }; - -class IPluginGameWrapper : public MOBase::IPluginGame, public boost::python::wrapper { - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame) +class IPluginGameWrapper : public MOBase::IPluginGame, + public boost::python::wrapper { + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame) public: - static constexpr const char* className = "IPluginGameWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "IPluginGameWrapper"; + using boost::python::wrapper::get_override; - virtual void detectGame() override; - virtual QString gameName() const override; - virtual void initializeProfile(const QDir &directory, ProfileSettings settings) const override; - virtual std::vector> listSaves(QDir folder) const override; - virtual bool isInstalled() const override; - virtual QIcon gameIcon() const override; - virtual QDir gameDirectory() const override; - virtual QDir dataDirectory() const override; - virtual void setGamePath(const QString &path) override; - virtual QDir documentsDirectory() const override; - virtual QDir savesDirectory() const override; - virtual QList executables() const override; - virtual QList executableForcedLoads() const override; - virtual QString steamAPPId() const override; - virtual QStringList primaryPlugins() const override; - virtual QStringList gameVariants() const override; - virtual void setGameVariant(const QString &variant) override; - virtual QString binaryName() const override; - virtual QString gameShortName() const override; - virtual QStringList primarySources() const override; - virtual QStringList validShortNames() const override; - virtual QString gameNexusName() const override; - virtual QStringList iniFiles() const override; - virtual QStringList DLCPlugins() const override; - virtual QStringList CCPlugins() const override; - virtual LoadOrderMechanism loadOrderMechanism() const override; - virtual SortMechanism sortMechanism() const override; - virtual int nexusModOrganizerID() const override; - virtual int nexusGameID() const override; - virtual bool looksValid(QDir const &dir) const override; - virtual QString gameVersion() const override; - virtual QString getLauncherName() const override; + virtual void detectGame() override; + virtual QString gameName() const override; + virtual void initializeProfile(const QDir& directory, + ProfileSettings settings) const override; + virtual std::vector> + listSaves(QDir folder) const override; + virtual bool isInstalled() const override; + virtual QIcon gameIcon() const override; + virtual QDir gameDirectory() const override; + virtual QDir dataDirectory() const override; + virtual void setGamePath(const QString& path) override; + virtual QDir documentsDirectory() const override; + virtual QDir savesDirectory() const override; + virtual QList executables() const override; + virtual QList + executableForcedLoads() const override; + virtual QString steamAPPId() const override; + virtual QStringList primaryPlugins() const override; + virtual QStringList gameVariants() const override; + virtual void setGameVariant(const QString& variant) override; + virtual QString binaryName() const override; + virtual QString gameShortName() const override; + virtual QStringList primarySources() const override; + virtual QStringList validShortNames() const override; + virtual QString gameNexusName() const override; + virtual QStringList iniFiles() const override; + virtual QStringList DLCPlugins() const override; + virtual QStringList CCPlugins() const override; + virtual LoadOrderMechanism loadOrderMechanism() const override; + virtual SortMechanism sortMechanism() const override; + virtual int nexusModOrganizerID() const override; + virtual int nexusGameID() const override; + virtual bool looksValid(QDir const& dir) const override; + virtual QString gameVersion() const override; + virtual QString getLauncherName() const override; - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(0) + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(0) protected: - // Apparently, Python developers interpret an underscore in a function name as it being protected - virtual std::map featureList() const override; + // Apparently, Python developers interpret an underscore in a function name as it + // being protected + virtual std::map featureList() const override; - // Thankfully, the default implementation of the templated 'T *feature()' function should allow us to get away without overriding it. + // Thankfully, the default implementation of the templated 'T *feature()' function + // should allow us to get away without overriding it. }; +#define COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS \ +public: \ + using IPluginInstaller::parentWidget; \ + using IPluginInstaller::manager; \ + virtual unsigned int priority() const override; \ + virtual bool isManualInstaller() const override; \ + virtual void onInstallationStart(QString const& archive, bool reinstallation, \ + MOBase::IModInterface* currentMod) override; \ + void onInstallationStart_Default(QString const& archive, bool reinstallation, \ + MOBase::IModInterface* currentMod) \ + { \ + return IPluginInstaller::onInstallationStart(archive, reinstallation, \ + currentMod); \ + } \ + virtual void onInstallationEnd(EInstallResult result, \ + MOBase::IModInterface* newMod) override; \ + void onInstallationEnd_Default(EInstallResult result, \ + MOBase::IModInterface* newMod) \ + { \ + return IPluginInstaller::onInstallationEnd(result, newMod); \ + } \ + virtual bool isArchiveSupported(std::shared_ptr tree) \ + const override; -#define COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS public: \ -using IPluginInstaller::parentWidget; \ -using IPluginInstaller::manager; \ -virtual unsigned int priority() const override; \ -virtual bool isManualInstaller() const override; \ -virtual void onInstallationStart(QString const& archive, bool reinstallation, MOBase::IModInterface* currentMod) override; \ -void onInstallationStart_Default(QString const& archive, bool reinstallation, MOBase::IModInterface* currentMod) { \ - return IPluginInstaller::onInstallationStart(archive, reinstallation, currentMod); } \ -virtual void onInstallationEnd(EInstallResult result, MOBase::IModInterface* newMod) override; \ -void onInstallationEnd_Default(EInstallResult result, MOBase::IModInterface* newMod) { \ - return IPluginInstaller::onInstallationEnd(result, newMod); } \ -virtual bool isArchiveSupported(std::shared_ptr tree) const override; +class IPluginInstallerSimpleWrapper + : public MOBase::IPluginInstallerSimple, + public boost::python::wrapper { + Q_OBJECT + Q_INTERFACES( + MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerSimple) - -class IPluginInstallerSimpleWrapper : public MOBase::IPluginInstallerSimple, public boost::python::wrapper -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerSimple) - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS - COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS + COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS public: - static constexpr const char* className = "IPluginInstallerSimpleWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "IPluginInstallerSimpleWrapper"; + using boost::python::wrapper::get_override; - virtual EInstallResult install(MOBase::GuessedValue& modName, std::shared_ptr& tree, - QString& version, int& nexusID) override; + virtual EInstallResult install(MOBase::GuessedValue& modName, + std::shared_ptr& tree, + QString& version, int& nexusID) override; }; -class IPluginInstallerCustomWrapper : public MOBase::IPluginInstallerCustom, public boost::python::wrapper -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) +class IPluginInstallerCustomWrapper + : public MOBase::IPluginInstallerCustom, + public boost::python::wrapper { + Q_OBJECT + Q_INTERFACES( + MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS - COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS + COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS public: - static constexpr const char* className = "IPluginInstallerCustomWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "IPluginInstallerCustomWrapper"; + using boost::python::wrapper::get_override; - virtual bool isArchiveSupported(const QString &archiveName) const override; - virtual std::set supportedExtensions() const override; - virtual EInstallResult install(MOBase::GuessedValue &modName, QString gameName, const QString &archiveName, - const QString &version, int modID) override; + virtual bool isArchiveSupported(const QString& archiveName) const override; + virtual std::set supportedExtensions() const override; + virtual EInstallResult install(MOBase::GuessedValue& modName, + QString gameName, const QString& archiveName, + const QString& version, int modID) override; }; +class IPluginModPageWrapper : public MOBase::IPluginModPage, + public boost::python::wrapper { + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginModPage) -class IPluginModPageWrapper : public MOBase::IPluginModPage, public boost::python::wrapper -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginModPage) - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: - static constexpr const char* className = "IPluginModPageWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "IPluginModPageWrapper"; + using boost::python::wrapper::get_override; - // Bring in public scope: - using IPluginModPage::parentWidget; + // Bring in public scope: + using IPluginModPage::parentWidget; - virtual QString displayName() const override; - virtual QIcon icon() const override; - virtual QUrl pageURL() const override; - virtual bool useIntegratedBrowser() const override; - virtual bool handlesDownload(const QUrl &pageURL, const QUrl &downloadURL, MOBase::ModRepositoryFileInfo &fileInfo) const override; - virtual void setParentWidget(QWidget *widget) override; + virtual QString displayName() const override; + virtual QIcon icon() const override; + virtual QUrl pageURL() const override; + virtual bool useIntegratedBrowser() const override; + virtual bool + handlesDownload(const QUrl& pageURL, const QUrl& downloadURL, + MOBase::ModRepositoryFileInfo& fileInfo) const override; + virtual void setParentWidget(QWidget* widget) override; - void setParentWidget_Default(QWidget* parent) { - IPluginModPage::setParentWidget(parent); - } + void setParentWidget_Default(QWidget* parent) + { + IPluginModPage::setParentWidget(parent); + } }; +class IPluginPreviewWrapper : public MOBase::IPluginPreview, + public boost::python::wrapper { + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginPreview) -class IPluginPreviewWrapper : public MOBase::IPluginPreview, public boost::python::wrapper -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginPreview) - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: - static constexpr const char* className = "IPluginPreviewWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "IPluginPreviewWrapper"; + using boost::python::wrapper::get_override; - virtual std::set supportedExtensions() const override; - virtual QWidget *genFilePreview(const QString &fileName, const QSize &maxSize) const override; + virtual std::set supportedExtensions() const override; + virtual QWidget* genFilePreview(const QString& fileName, + const QSize& maxSize) const override; }; +class IPluginToolWrapper : public MOBase::IPluginTool, + public boost::python::wrapper { + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool) -class IPluginToolWrapper: public MOBase::IPluginTool, public boost::python::wrapper -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool) - - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: - static constexpr const char* className = "IPluginToolWrapper"; - using boost::python::wrapper::get_override; + static constexpr const char* className = "IPluginToolWrapper"; + using boost::python::wrapper::get_override; - // Bring in public scope: - using IPluginTool::parentWidget; + // Bring in public scope: + using IPluginTool::parentWidget; - virtual QString displayName() const override; - virtual QString tooltip() const override; - virtual QIcon icon() const override; - virtual void setParentWidget(QWidget *parent) override; + virtual QString displayName() const override; + virtual QString tooltip() const override; + virtual QIcon icon() const override; + virtual void setParentWidget(QWidget* parent) override; - void setParentWidget_Default(QWidget* parent) { - IPluginTool::setParentWidget(parent); - } + void setParentWidget_Default(QWidget* parent) + { + IPluginTool::setParentWidget(parent); + } public Q_SLOTS: - virtual void display() const override; + virtual void display() const override; }; - - - -#endif // PROXYPLUGINWRAPPERS_H +#endif // PROXYPLUGINWRAPPERS_H diff --git a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_enum.h b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_enum.h index 57969f7..45afd0c 100644 --- a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_enum.h +++ b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_enum.h @@ -34,8 +34,8 @@ namespace pybind11::detail::qt { return true; } - auto pyenum = get_attr_rec(EnumData::package.text, - EnumData::name.text); + auto pyenum = + get_attr_rec(EnumData::package.text, EnumData::name.text); if (isinstance(src, pyenum)) { value = static_cast(src.attr("value").cast()); @@ -49,8 +49,8 @@ namespace pybind11::detail::qt { pybind11::return_value_policy /* policy */, pybind11::handle /* parent */) { - auto pyenum = get_attr_rec(EnumData::package.text, - EnumData::name.text); + auto pyenum = + get_attr_rec(EnumData::package.text, EnumData::name.text); return pyenum(static_cast(src)); } }; diff --git a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_qmap.h b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_qmap.h index 71d8fe5..ea743b7 100644 --- a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_qmap.h +++ b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_qmap.h @@ -43,14 +43,13 @@ namespace pybind11::detail::qt { return_value_policy policy_key = policy; return_value_policy policy_value = policy; if (!std::is_lvalue_reference::value) { - policy_key = - return_value_policy_override::policy(policy_key); + policy_key = return_value_policy_override::policy(policy_key); policy_value = return_value_policy_override::policy(policy_value); } for (auto it = src.begin(); it != src.end(); ++it) { - auto key = reinterpret_steal(key_conv::cast( - forward_like(it.key()), policy_key, parent)); + auto key = reinterpret_steal( + key_conv::cast(forward_like(it.key()), policy_key, parent)); auto value = reinterpret_steal(value_conv::cast( forward_like(it.value()), policy_value, parent)); if (!key || !value) { @@ -66,6 +65,6 @@ namespace pybind11::detail::qt { const_name("]")); }; -} // namespace pybind11::qt::detail +} // namespace pybind11::detail::qt #endif diff --git a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_sip.cpp b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_sip.cpp index d79109d..edc48db 100644 --- a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_sip.cpp +++ b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_sip.cpp @@ -31,8 +31,7 @@ namespace pybind11::detail::qt { exception = exp_str.cast(); } PyErr_Restore(type, value, traceback); - throw std::runtime_error{"Failed to load SIP API: " + - exception}; + throw std::runtime_error{"Failed to load SIP API: " + exception}; } sipApi = (const sipAPIDef*)PyCapsule_Import("PyQt6.sip._C_API", 0); @@ -49,14 +48,12 @@ namespace pybind11::detail::qt { py::object tb(py::module_::import("traceback")); py::object fmt_exp(tb.attr("format_exception")); py::object exp_list(fmt_exp(h_type, h_val, h_tb)); - py::object exp_str( - py::str("\n").attr("join")(exp_list)); + py::object exp_str(py::str("\n").attr("join")(exp_list)); exception = exp_str.cast(); } PyErr_Restore(type, value, traceback); } - throw std::runtime_error{"Failed to load SIP API: " + - exception}; + throw std::runtime_error{"Failed to load SIP API: " + exception}; } } return sipApi; diff --git a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_sip.h b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_sip.h index aab0efe..a01f4bf 100644 --- a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_sip.h +++ b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_sip.h @@ -50,17 +50,15 @@ namespace pybind11::detail::qt { } } - template < - class T, - std::enable_if_t && !is_pointer, int> = 0> + template && !is_pointer, int> = 0> operator T&() { return value; } - template < - class T, - std::enable_if_t && !is_pointer, int> = 0> + template && !is_pointer, int> = 0> operator T&&() && { return std::move(value); @@ -88,12 +86,10 @@ namespace pybind11::detail::qt { // sipAPI()->api_transfer_to(objPtr, Py_None); // void* data = nullptr; - if (PyObject_TypeCheck(src.ptr(), - qt::sipAPI()->api_simplewrapper_type)) { + if (PyObject_TypeCheck(src.ptr(), qt::sipAPI()->api_simplewrapper_type)) { data = reinterpret_cast(src.ptr())->data; } - else if (PyObject_TypeCheck(src.ptr(), - qt::sipAPI()->api_wrapper_type)) { + else if (PyObject_TypeCheck(src.ptr(), qt::sipAPI()->api_wrapper_type)) { data = reinterpret_cast(src.ptr())->super.data; } @@ -113,8 +109,7 @@ namespace pybind11::detail::qt { template < typename T, - std::enable_if_t>::value, - int> = 0> + std::enable_if_t>::value, int> = 0> static handle cast(T* src, return_value_policy policy, handle parent) { // note: when QClass is a pointer type, e.g. a QWidget*, T is a @@ -132,8 +127,7 @@ namespace pybind11::detail::qt { return cast(*src, policy, parent); } - static pybind11::handle cast(QClass src, - pybind11::return_value_policy policy, + static pybind11::handle cast(QClass src, pybind11::return_value_policy policy, pybind11::handle /* parent */) { if constexpr (is_pointer) { @@ -157,10 +151,10 @@ namespace pybind11::detail::qt { else if (std::is_copy_assignable_v) { // we send to SIP a newly allocated object, and transfer the // owernship to it - sipData = new QClass( - policy == ::pybind11::return_value_policy::take_ownership - ? std::move(src) - : src); + sipData = + new QClass(policy == ::pybind11::return_value_policy::take_ownership + ? std::move(src) + : src); } else { sipData = &src; diff --git a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_utils.cpp b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_utils.cpp index c8cafeb..4cd98d0 100644 --- a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_utils.cpp +++ b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_utils.cpp @@ -2,8 +2,7 @@ namespace pybind11::detail::qt { - pybind11::object get_attr_rec(std::string_view package, - std::string_view path) + pybind11::object get_attr_rec(std::string_view package, std::string_view path) { return module_::import("operator") diff --git a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_utils.h b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_utils.h index 71171d9..9f0eed9 100644 --- a/src/runner-pybind11/pybind11_qt/details/pybind11_qt_utils.h +++ b/src/runner-pybind11/pybind11_qt/details/pybind11_qt_utils.h @@ -1,6 +1,7 @@ #ifndef PYTHON_PYBIND11_QT_DETAILS_UTILS_HPP #define PYTHON_PYBIND11_QT_DETAILS_UTILS_HPP +#include #include #include @@ -39,17 +40,15 @@ namespace pybind11::detail::qt { * * @return the object at the given path in the given module */ - pybind11::object get_attr_rec(std::string_view package, - std::string_view path); + pybind11::object get_attr_rec(std::string_view package, std::string_view path); /** * @brief Conditional add_pointer if T is non-copyable. * */ template - using ptr_if_non_copy_t = - std::conditional_t, QClass, - std::add_pointer_t>; + using ptr_if_non_copy_t = std::conditional_t, + QClass, std::add_pointer_t>; } // namespace pybind11::detail::qt diff --git a/src/runner-pybind11/pybind11_qt/pybind11_qt.h b/src/runner-pybind11/pybind11_qt/pybind11_qt.h index f5cd828..eafaffb 100644 --- a/src/runner-pybind11/pybind11_qt/pybind11_qt.h +++ b/src/runner-pybind11/pybind11_qt/pybind11_qt.h @@ -7,4 +7,39 @@ #include "pybind11_qt_objects.h" #include "pybind11_qt_qflags.h" +namespace pybind11::qt { + + /** + * @brief Add Qt "delegate" to the given class. + * + * This function defines two methods: __getattr__ and name, where name will + * simply return the PyQtX object as a QClass* object, while __getattr__ + * will delegate to the underlying QClass object when required. + * + * This allow access to Qt interface for object exposed using boost::python + * (e.g., signals, methods from QObject or QWidget, etc.). + * + * @param pyclass Python class to define the methods on. + * @param name Name of the method to retrieve the underlying object. + * + * @tparam QClass Name of the Qt class, cannot be deduced. + * @tparam Class Class being wrapped, deduced. + * @tparam Args... Arguments of the class template parameters, deduced. + */ + template + auto& add_qt_delegate(pybind11::class_& pyclass, const char* name) + { + return pyclass + .def(name, + [](Class* w) -> QClass* { + return w; + }) + .def( + "__getattr__", +[](Class* w, pybind11::str str) -> pybind11::object { + return pybind11::cast((QClass*)w).attr(str); + }); + } + +} // namespace pybind11::qt + #endif diff --git a/src/runner-pybind11/pybind11_qt/pybind11_qt_basic.cpp b/src/runner-pybind11/pybind11_qt/pybind11_qt_basic.cpp index 54323ef..7d12d25 100644 --- a/src/runner-pybind11/pybind11_qt/pybind11_qt_basic.cpp +++ b/src/runner-pybind11/pybind11_qt/pybind11_qt_basic.cpp @@ -1,19 +1,54 @@ #include "pybind11_qt_basic.h" +#include + +#include + +#include "details/pybind11_qt_utils.h" + namespace pybind11::detail { + template + QString qstring_from_stdstring(std::basic_string const& s) + { + if constexpr (std::is_same_v) { + return QString::fromStdString(s); + } + else if constexpr (std::is_same_v) { + return QString::fromStdWString(s); + } + else if constexpr (std::is_same_v) { + return QString::fromStdU16String(s); + } + else if constexpr (std::is_same_v) { + return QString::fromStdU32String(s); + } + } + /** * Conversion part 1 (Python->C++): convert a PyObject into a QString * instance or return false upon failure. The second argument * indicates whether implicit conversions should be applied. */ - bool type_caster::load(handle src, bool) + bool type_caster::load(handle src, bool implicit) { PyObject* objPtr = src.ptr(); if (!PyBytes_Check(objPtr) && !PyUnicode_Check(objPtr)) { +#ifndef PYBIND11_QT_QSTRING_NOFS + // try converting from os.PathLike + type_caster path_caster; + if (!path_caster.load(src, implicit)) { + return false; + } + + value = qstring_from_stdstring((*path_caster).native()); + return true; +#else + // do not try to convert from os.PathLike return false; +#endif } // Ensure the string uses 8-bit characters @@ -38,8 +73,7 @@ namespace pybind11::detail { * ``return_value_policy::reference_internal``) and are generally * ignored by implicit casters. */ - handle type_caster::cast(QString src, - return_value_policy /* policy */, + handle type_caster::cast(QString src, return_value_policy /* policy */, handle /* parent */) { static_assert(sizeof(QChar) == 2); @@ -102,11 +136,9 @@ namespace pybind11::detail { // We need to check for StringList here because these are not considered // List since List is QList will StringList is QList: case QVariant::StringList: - return type_caster::cast(var.toStringList(), policy, - parent); + return type_caster::cast(var.toStringList(), policy, parent); case QVariant::List: - return type_caster::cast(var.toList(), policy, - parent); + return type_caster::cast(var.toList(), policy, parent); case QVariant::Map: // return // type_caster::cast(var.toList(), // policy, parent); diff --git a/src/runner-pybind11/pybind11_qt/pybind11_qt_basic.h b/src/runner-pybind11/pybind11_qt/pybind11_qt_basic.h index 9576bd7..f4bf3ff 100644 --- a/src/runner-pybind11/pybind11_qt/pybind11_qt_basic.h +++ b/src/runner-pybind11/pybind11_qt/pybind11_qt_basic.h @@ -16,8 +16,7 @@ namespace pybind11::detail { bool load(handle src, bool); - static handle cast(QString src, return_value_policy policy, - handle parent); + static handle cast(QString src, return_value_policy policy, handle parent); }; // QVariant - this needs to be defined BEFORE QVariantList @@ -29,8 +28,7 @@ namespace pybind11::detail { bool load(handle src, bool); - static handle cast(QVariant var, return_value_policy policy, - handle parent); + static handle cast(QVariant var, return_value_policy policy, handle parent); }; } // namespace pybind11::detail diff --git a/src/runner-pybind11/pybind11_qt/pybind11_qt_enums.h b/src/runner-pybind11/pybind11_qt/pybind11_qt_enums.h index 7048fb9..b6e6483 100644 --- a/src/runner-pybind11/pybind11_qt/pybind11_qt_enums.h +++ b/src/runner-pybind11/pybind11_qt/pybind11_qt_enums.h @@ -7,25 +7,24 @@ #include "details/pybind11_qt_enum.h" #include "details/pybind11_qt_utils.h" -#define PYQT_ENUM(QPackage, QEnum) \ - namespace qt { \ - template <> \ - struct EnumData { \ - constexpr static const auto package = \ - const_name("PyQt6.") + const_name(#QPackage); \ - constexpr static const auto name = qt_name_cpp2py(#QEnum); \ - }; \ - } \ - template <> \ - struct type_caster : qt::qt_enum_caster { \ +#define PYQT_ENUM(QPackage, QEnum) \ + namespace qt { \ + template <> \ + struct EnumData { \ + constexpr static const auto package = \ + const_name("PyQt6.") + const_name(#QPackage); \ + constexpr static const auto name = qt_name_cpp2py(#QEnum); \ + }; \ + } \ + template <> \ + struct type_caster : qt::qt_enum_caster { \ } namespace pybind11::detail { PYQT_ENUM(QtCore, Qt::GlobalColor); - static_assert(std::string_view("PyQt6.QtCore.Qt.GlobalColor") == - std::string_view(type_caster::name.text)); PYQT_ENUM(QtWidgets, QMessageBox::Icon); + PYQT_ENUM(QtWidgets, QMessageBox::StandardButton); } // namespace pybind11::detail diff --git a/src/runner-pybind11/pybind11_qt/pybind11_qt_objects.h b/src/runner-pybind11/pybind11_qt/pybind11_qt_objects.h index 97330f9..d83398c 100644 --- a/src/runner-pybind11/pybind11_qt/pybind11_qt_objects.h +++ b/src/runner-pybind11/pybind11_qt/pybind11_qt_objects.h @@ -16,19 +16,19 @@ #include "details/pybind11_qt_sip.h" #include "details/pybind11_qt_utils.h" -#define PYQT_CLASS(QModule, QClass) \ - namespace qt { \ - template <> \ - struct MetaData { \ - constexpr static const auto class_name = #QClass; \ - constexpr static const auto python_name = \ - const_name("PyQt6.") + const_name(#QModule) + \ - const_name(".") + const_name(#QClass); \ - }; \ - } \ - template <> \ - struct type_caster> \ - : qt::qt_type_caster> { \ +#define PYQT_CLASS(QModule, QClass) \ + namespace qt { \ + template <> \ + struct MetaData { \ + constexpr static const auto class_name = #QClass; \ + constexpr static const auto python_name = \ + const_name("PyQt6.") + const_name(#QModule) + const_name(".") + \ + const_name(#QClass); \ + }; \ + } \ + template <> \ + struct type_caster> \ + : qt::qt_type_caster> { \ } namespace pybind11::detail { diff --git a/src/runner-pybind11/pybind11_qt/pybind11_qt_qflags.h b/src/runner-pybind11/pybind11_qt/pybind11_qt_qflags.h index 0d73754..c9f4eca 100644 --- a/src/runner-pybind11/pybind11_qt/pybind11_qt_qflags.h +++ b/src/runner-pybind11/pybind11_qt/pybind11_qt_qflags.h @@ -11,8 +11,7 @@ namespace pybind11::detail { // template struct type_caster> { - PYBIND11_TYPE_CASTER(QFlags, const_name("QFlags[") + - make_caster::name + + PYBIND11_TYPE_CASTER(QFlags, const_name("QFlags[") + make_caster::name + const_name("]")); /** @@ -45,14 +44,13 @@ namespace pybind11::detail { * ``return_value_policy::reference_internal``) and are generally * ignored by implicit casters. */ - static handle cast(QFlags const& src, - return_value_policy /* policy */, + static handle cast(QFlags const& src, return_value_policy /* policy */, handle /* parent */) { return PyLong_FromLong(static_cast(src)); } }; -} // namespace pybind11::details +} // namespace pybind11::detail #endif diff --git a/src/runner-pybind11/pyfiletree.h b/src/runner-pybind11/pyfiletree.h deleted file mode 100644 index 7a31039..0000000 --- a/src/runner-pybind11/pyfiletree.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef MO2_PYTHON_FILETREE_H -#define MO2_PYTHON_FILETREE_H - -#include - -#include - -namespace pybind11 -{ - template <> - struct polymorphic_type_hook - { - static const void *get(const MOBase::FileTreeEntry *src, const std::type_info *&type); - }; -} // namespace pybind11 - - -namespace mo2::python { - - /** - * @brief Add bindings for FileTreeEntry andIFileTree to the given module. - * - * @param mobase Module to add the bindings to. - */ - void add_ifiletree_bindings(pybind11::module_ &m); - - /** - * @brief Add makeTree() function to the given module, useful for debugging. - * - * @param mobase Module to add the function to. - */ - void add_make_tree_function(pybind11::module_ &m); - -} - -#endif diff --git a/src/runner-pybind11/pythonrunner.cpp b/src/runner-pybind11/pythonrunner.cpp index 5df1e58..e83ab3a 100644 --- a/src/runner-pybind11/pythonrunner.cpp +++ b/src/runner-pybind11/pythonrunner.cpp @@ -3,79 +3,39 @@ #pragma warning(disable : 4100) #pragma warning(disable : 4996) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include + +#include #include #include #include #include -#include - -#include -#include #include #include #include +#include + +#include "pybind11_qt/pybind11_qt.h" + +#include +#include #include "error.h" -#include "pybind11_qt/pybind11_qt.h" -#include "wrappers/uibase_wrappers.h" -// #include "gamefeatureswrappers.h" -// #include "proxypluginwrappers.h" -#include "pyfiletree.h" #include "pythonutils.h" -// #include "shared_ptr_converter.h" -// #include "sipApiAccess.h" -// #include "tuple_helper.h" -// #include "uibasewrappers.h" -// #include "variant_helper.h" -// #include "widgets.h" +#include "wrappers/pyfiletree.h" +#include "wrappers/wrappers.h" + +// TODO: remove these include (only for testing) +#include +#include +#include using namespace MOBase; - -// namespace bpy = boost::python; -// namespace mp11 = boost::mp11; namespace py = pybind11; -/** - * This macro should be used within a py::class_ declaration and will define two - * methods: __getattr__ and Name, where Name will simply return the object as a - * QClass* object, while __getattr__ will delegate to the underlying QClass - * object when required. - * - * This allow access to Qt interface for object exposed using boost::python - * (e.g., signals, methods from QObject or QWidget, etc.). - */ -template -auto& add_qt_delegate(py::class_& pyclass, const char* name) -{ - return pyclass - .def(name, - [](Class* w) -> QClass* { - return w; - }) - .def( - "__getattr__", +[](Class* w, py::str str) -> py::object { - return py::cast((QClass*)w).attr(str); - }); -} - PYBIND11_MODULE(mobase, m) { using namespace mo2::python; @@ -83,6 +43,38 @@ PYBIND11_MODULE(mobase, m) py::module_::import("PyQt6.QtCore"); py::module_::import("PyQt6.QtWidgets"); + // bindings + // + mo2::python::add_basic_bindings(m); + mo2::python::add_wrapper_bindings(m); + mo2::python::add_plugins_bindings(m); + mo2::python::add_game_feature_bindings(m); + + // widgets + // + py::module_ widgets( + py::reinterpret_borrow(PyImport_AddModule("mobase.widgets"))); + m.attr("widgets") = widgets; + mo2::python::add_widget_bindings(widgets); + + // functions + // + m.def("getFileVersion", &MOBase::getFileVersion, py::arg("filepath")); + m.def("getProductVersion", &MOBase::getProductVersion, py::arg("executable")); + m.def("getIconForExecutable", &MOBase::iconForExecutable, py::arg("executable")); + + // expose MoVariant - MoVariant is a fake object whose only purpose is to be + // used as a type-hint on the python side (e.g., def foo(x: + // mobase.MoVariant)) + // + // the real MoVariant is defined in the generated stubs, since it's only + // relevant when doing type-checking, but this needs to be defined, + // otherwise MoVariant is not found when actually running plugins through + // MO2, making them crash + m.attr("MoVariant") = py::none(); + + // == BEGIN TESTS == + py::detail::type_caster t1; py::detail::type_caster t2; @@ -136,8 +128,7 @@ PYBIND11_MODULE(mobase, m) QString getFilepath() const override { return "filepath"; } QDateTime getCreationTime() const override { - return QDateTime::fromString("2022-02-15T12:33:45", - Qt::ISODate); + return QDateTime::fromString("2022-02-15T12:33:45", Qt::ISODate); } QString getName() const override { return "name"; } QString getSaveGroupIdentifier() const override { return "group"; } @@ -151,8 +142,7 @@ PYBIND11_MODULE(mobase, m) QString getFilepath() const override { return "filepath-c++"; } QDateTime getCreationTime() const override { - return QDateTime::fromString("2022-02-15T12:33:45", - Qt::ISODate); + return QDateTime::fromString("2022-02-15T12:33:45", Qt::ISODate); } QString getName() const override { return "name"; } QString getSaveGroupIdentifier() const override { return "group"; } @@ -163,8 +153,7 @@ PYBIND11_MODULE(mobase, m) }); m.def("testSaveGameRef", [](const ISaveGame& game) { - std::cout << "getFilepath(): " << game.getFilepath().toStdString() - << "\n"; + std::cout << "getFilepath(): " << game.getFilepath().toStdString() << "\n"; std::cout << "getCreationTime(): " << game.getCreationTime().toString().toStdString() << "\n"; std::cout << "getName(): " << game.getName().toStdString() << "\n"; @@ -175,15 +164,14 @@ PYBIND11_MODULE(mobase, m) }); m.def("testSaveGamePtr", [](const ISaveGame* game) { - std::cout << "getFilepath(): " << game->getFilepath().toStdString() - << "\n"; + std::cout << "getFilepath(): " << game->getFilepath().toStdString() << "\n"; std::cout << "getCreationTime(): " << game->getCreationTime().toString().toStdString() << "\n"; std::cout << "getName(): " << game->getName().toStdString() << "\n"; std::cout << "getSaveGroupIdentifier(): " << game->getSaveGroupIdentifier().toStdString() << "\n"; - std::cout << "allFiles(): [ " - << game->allFiles().join(" ").toStdString() << " ]\n"; + std::cout << "allFiles(): [ " << game->allFiles().join(" ").toStdString() + << " ]\n"; }); m.def("testWidget1", [](QWidget* w) { @@ -201,582 +189,6 @@ PYBIND11_MODULE(mobase, m) return w; }); - // - // Class declarations: - // - - py::enum_(m, "ReleaseType") - .value("final", MOBase::VersionInfo::RELEASE_FINAL) - .value("candidate", MOBase::VersionInfo::RELEASE_CANDIDATE) - .value("beta", MOBase::VersionInfo::RELEASE_BETA) - .value("alpha", MOBase::VersionInfo::RELEASE_ALPHA) - .value("prealpha", MOBase::VersionInfo::RELEASE_PREALPHA) - - .value("FINAL", MOBase::VersionInfo::RELEASE_FINAL) - .value("CANDIDATE", MOBase::VersionInfo::RELEASE_CANDIDATE) - .value("BETA", MOBase::VersionInfo::RELEASE_BETA) - .value("ALPHA", MOBase::VersionInfo::RELEASE_ALPHA) - .value("PRE_ALPHA", MOBase::VersionInfo::RELEASE_PREALPHA); - - py::enum_(m, "VersionScheme") - .value("discover", MOBase::VersionInfo::SCHEME_DISCOVER) - .value("regular", MOBase::VersionInfo::SCHEME_REGULAR) - .value("decimalmark", MOBase::VersionInfo::SCHEME_DECIMALMARK) - .value("numbersandletters", - MOBase::VersionInfo::SCHEME_NUMBERSANDLETTERS) - .value("date", MOBase::VersionInfo::SCHEME_DATE) - .value("literal", MOBase::VersionInfo::SCHEME_LITERAL) - - .value("DISCOVER", MOBase::VersionInfo::SCHEME_DISCOVER) - .value("REGULAR", MOBase::VersionInfo::SCHEME_REGULAR) - .value("DECIMAL_MARK", MOBase::VersionInfo::SCHEME_DECIMALMARK) - .value("NUMBERS_AND_LETTERS", - MOBase::VersionInfo::SCHEME_NUMBERSANDLETTERS) - .value("DATE", MOBase::VersionInfo::SCHEME_DATE) - .value("LITERAL", MOBase::VersionInfo::SCHEME_LITERAL); - - py::class_(m, "VersionInfo") - .def(py::init(), py::arg("value"), - py::arg("scheme") = VersionInfo::SCHEME_DISCOVER) - // note: order of the two init<> below is important because - // ReleaseType is a simple enum with an implicit int conversion. - .def(py::init(), - py::arg("major"), py::arg("minor"), py::arg("subminor"), - py::arg("subsubminor"), - py::arg("release_type") = VersionInfo::RELEASE_FINAL) - .def(py::init(), - py::arg("major"), py::arg("minor"), py::arg("subminor"), - py::arg("release_type") = VersionInfo::RELEASE_FINAL) - .def("clear", &VersionInfo::clear) - .def("parse", &VersionInfo::parse, py::arg("value"), - py::arg("scheme") = VersionInfo::SCHEME_DISCOVER, - py::arg("is_manual") = false) - .def("canonicalString", &VersionInfo::canonicalString) - .def("displayString", &VersionInfo::displayString, - py::arg("forced_segments") = 2) - .def("isValid", &VersionInfo::isValid) - .def("scheme", &VersionInfo::scheme) - .def("__str__", &VersionInfo::canonicalString) - .def(py::self < py::self) - .def(py::self > py::self) - .def(py::self <= py::self) - .def(py::self >= py::self) - .def(py::self != py::self) - .def(py::self == py::self); - - py::class_(m, "PluginSetting") - .def(py::init(), - py::arg("key"), py::arg("description"), py::arg("default_value")) - .def_readwrite("key", &PluginSetting::key) - .def_readwrite("description", &PluginSetting::description) - .def_readwrite("default_value", &PluginSetting::defaultValue); - - py::class_(m, "ExecutableInfo") - .def(py::init(), py::arg("title"), - py::arg("binary")) - .def("withArgument", &ExecutableInfo::withArgument, py::arg("argument")) - .def("withWorkingDirectory", &ExecutableInfo::withWorkingDirectory, - py::arg("directory")) - .def("withSteamAppId", &ExecutableInfo::withSteamAppId, - py::arg("app_id")) - .def("asCustom", &ExecutableInfo::asCustom) - .def("isValid", &ExecutableInfo::isValid) - .def("title", &ExecutableInfo::title) - .def("binary", &ExecutableInfo::binary) - .def("arguments", &ExecutableInfo::arguments) - .def("workingDirectory", &ExecutableInfo::workingDirectory) - .def("steamAppID", &ExecutableInfo::steamAppID) - .def("isCustom", &ExecutableInfo::isCustom); - - py::class_(m, "ExecutableForcedLoadSetting") - .def(py::init(), py::arg("process"), - py::arg("library")) - .def("withForced", &ExecutableForcedLoadSetting::withForced, - py::arg("forced")) - .def("withEnabled", &ExecutableForcedLoadSetting::withEnabled, - py::arg("enabled")) - .def("enabled", &ExecutableForcedLoadSetting::enabled) - .def("forced", &ExecutableForcedLoadSetting::forced) - .def("library", &ExecutableForcedLoadSetting::library) - .def("process", &ExecutableForcedLoadSetting::process); - - py::class_(m, "ISaveGame") - .def(py::init<>()) - .def("getFilepath", &ISaveGame::getFilepath) - .def("getCreationTime", &ISaveGame::getCreationTime) - .def("getName", &ISaveGame::getName) - .def("getSaveGroupIdentifier", &ISaveGame::getSaveGroupIdentifier) - .def("allFiles", &ISaveGame::allFiles); - - py::class_ iSaveGameInfoWidget( - m, "ISaveGameInfoWidget"); - iSaveGameInfoWidget.def(py::init<>()) - .def(py::init(), py::arg("parent")) - .def("setSave", &ISaveGameInfoWidget::setSave, py::arg("save")); - add_qt_delegate(iSaveGameInfoWidget, "widget"); - - // // Plugin requirements: - // auto iPluginRequirementClass = py::class_< - // IPluginRequirementWrapper, py::bases<>, - // boost::noncopyable>("IPluginRequirement"); - // { - // py::scope scope = iPluginRequirementClass; - - // py::class_("Problem", - // py::init((py::arg("short_description"), - // py::arg("long_description") = ""))) .def("shortDescription", - // &IPluginRequirement::Problem::shortDescription) - // .def("longDescription", - // &IPluginRequirement::Problem::longDescription); - - // iPluginRequirementClass - // .def("check", py::pure_virtual(&IPluginRequirement::check), - // py::arg("organizer")) - // ; - // } - - // py::class_("PluginRequirementFactory") - // // pluginDependency - // .def("pluginDependency", +[](QStringList const& pluginNames) { - // return PluginRequirementFactory::pluginDependency(pluginNames); - // }, py::arg("plugins")) - // .def("pluginDependency", +[](QString const& pluginName) { - // return PluginRequirementFactory::pluginDependency(pluginName); - // }, py::arg("plugin")) - // .staticmethod("pluginDependency") - // // gameDependency - // .def("gameDependency", +[](QStringList const& gameNames) { - // return PluginRequirementFactory::gameDependency(gameNames); - // }, py::arg("games")) - // .def("gameDependency", +[](QString const& gameNames) { - // return PluginRequirementFactory::gameDependency(gameNames); - // }, py::arg("game")) - // .staticmethod("gameDependency") - // // diagnose - // .def("diagnose", &PluginRequirementFactory::diagnose, - // py::arg("diagnose")) .staticmethod("diagnose") - // // basic - // .def("basic", &PluginRequirementFactory::basic, (py::arg("checker"), - // "description")) .staticmethod("basic"); - - // py::class_("FileInfo", py::init<>()) - // .add_property("filePath", - // +[](const IOrganizer::FileInfo& info) { return info.filePath; }, - // +[](IOrganizer::FileInfo& info, QString value) { info.filePath = - // value; - // }) - // .add_property("archive", - // +[](const IOrganizer::FileInfo& info) { return info.archive; }, - // +[](IOrganizer::FileInfo& info, QString value) { info.archive = - // value; - // }) - // .add_property("origins", - // +[](const IOrganizer::FileInfo& info) { return info.origins; }, - // +[](IOrganizer::FileInfo& info, QStringList value) { info.origins = - // value; }) - // ; - - // py::class_("IOrganizer", py::no_init) - // .def("createNexusBridge", &IOrganizer::createNexusBridge, - // py::return_value_policy()) - // .def("profileName", &IOrganizer::profileName) - // .def("profilePath", &IOrganizer::profilePath) - // .def("downloadsPath", &IOrganizer::downloadsPath) - // .def("overwritePath", &IOrganizer::overwritePath) - // .def("basePath", &IOrganizer::basePath) - // .def("modsPath", &IOrganizer::modsPath) - // .def("appVersion", &IOrganizer::appVersion) - // .def("createMod", &IOrganizer::createMod, - // py::return_value_policy(), - // py::arg("name")) .def("getGame", &IOrganizer::getGame, - // py::return_value_policy(), - // py::arg("name")) .def("modDataChanged", &IOrganizer::modDataChanged, - // py::arg("mod")) .def("isPluginEnabled", +[](IOrganizer* o, IPlugin* - // plugin) { return o->isPluginEnabled(plugin); }, py::arg("plugin")) - // .def("isPluginEnabled", +[](IOrganizer* o, QString const& plugin) { - // return o->isPluginEnabled(plugin); }, py::arg("plugin")) - // .def("pluginSetting", &IOrganizer::pluginSetting, - // (py::arg("plugin_name"), "key")) .def("setPluginSetting", - // &IOrganizer::setPluginSetting, (py::arg("plugin_name"), "key", - // "value")) .def("persistent", &IOrganizer::persistent, - // (py::arg("plugin_name"), "key", py::arg("default") = QVariant())) - // .def("setPersistent", &IOrganizer::setPersistent, - // (py::arg("plugin_name"), "key", "value", py::arg("sync") = true)) - // .def("pluginDataPath", &IOrganizer::pluginDataPath) - // .def("installMod", &IOrganizer::installMod, - // py::return_value_policy(), - // (py::arg("filename"), py::arg("name_suggestion") = "")) - // .def("resolvePath", &IOrganizer::resolvePath, py::arg("filename")) - // .def("listDirectories", &IOrganizer::listDirectories, - // py::arg("directory")) - - // // Provide multiple overloads of findFiles: - // .def("findFiles", +[](const IOrganizer* o, QString const& p, - // std::function f) { return o->findFiles(p, f); - // }, - // (py::arg("path"), "filter")) - - // // In C++, it is possible to create a QStringList implicitly from a - // single QString. This is not possible with the current - // // converters in python (and I do not think it is a good idea to have - // it everywhere), but here it is nice to be able to - // // pass a single string, so we add an extra overload. - // // Important: the order matters, because a Python string can be - // converted to a QStringList since it is a sequence of - // // single-character strings: - // .def("findFiles", +[](const IOrganizer* o, QString const& p, const - // QStringList& gf) { return o->findFiles(p, gf); }, - // (py::arg("path"), "patterns")) - // .def("findFiles", +[](const IOrganizer* o, QString const& p, const - // QString& f) { return o->findFiles(p, QStringList{ f }); }, - // (py::arg("path"), "pattern")) - - // .def("getFileOrigins", &IOrganizer::getFileOrigins, - // py::arg("filename")) .def("findFileInfos", - // &IOrganizer::findFileInfos, (py::arg("path"), "filter")) - - // .def("virtualFileTree", &IOrganizer::virtualFileTree) - - // .def("downloadManager", &IOrganizer::downloadManager, - // py::return_value_policy()) - // .def("pluginList", &IOrganizer::pluginList, - // py::return_value_policy()) - // .def("modList", &IOrganizer::modList, - // py::return_value_policy()) - // .def("profile", &IOrganizer::profile, - // py::return_value_policy()) - - // // Custom implementation for startApplication and waitForApplication - // because 1) HANDLE (= void*) is not properly - // // converted from/to python, and 2) we need to convert the by-ptr - // argument to a return-tuple for waitForApplication: - // .def("startApplication", - // +[](IOrganizer* o, const QString& executable, const QStringList& - // args, const QString& cwd, const QString& profile, - // const QString& forcedCustomOverwrite, bool ignoreCustomOverwrite) - // { - // return (std::uintptr_t) o->startApplication(executable, args, - // cwd, profile, forcedCustomOverwrite, ignoreCustomOverwrite); - // }, (py::arg("executable"), (py::arg("args") = QStringList()), - // (py::arg("cwd") = ""), (py::arg("profile") = ""), - // (py::arg("forcedCustomOverwrite") = ""), - // (py::arg("ignoreCustomOverwrite") = false)), - // py::return_value_policy()) - // .def("waitForApplication", +[](IOrganizer *o, std::uintptr_t handle, - // bool refresh) { - // DWORD returnCode; - // bool result = o->waitForApplication((HANDLE)handle, refresh, - // &returnCode); return std::make_tuple(result, returnCode); - // }, (py::arg("handle"), py::arg("refresh") = true)) - // .def("refresh", &IOrganizer::refresh, (py::arg("save_changes") = - // true)) .def("managedGame", &IOrganizer::managedGame, - // py::return_value_policy()) - - // .def("onAboutToRun", &IOrganizer::onAboutToRun, py::arg("callback")) - // .def("onFinishedRun", &IOrganizer::onFinishedRun, - // py::arg("callback")) .def("onUserInterfaceInitialized", - // &IOrganizer::onUserInterfaceInitialized, py::arg("callback")) - // .def("onProfileCreated", &IOrganizer::onProfileCreated, - // py::arg("callback")) .def("onProfileRenamed", - // &IOrganizer::onProfileRenamed, py::arg("callback")) - // .def("onProfileRemoved", &IOrganizer::onProfileRemoved, - // py::arg("callback")) .def("onProfileChanged", - // &IOrganizer::onProfileChanged, py::arg("callback")) - - // .def("onPluginSettingChanged", &IOrganizer::onPluginSettingChanged, - // py::arg("callback")) .def("onPluginEnabled", +[](IOrganizer* o, - // std::function const& func) { - // o->onPluginEnabled(func); - // }, py::arg("callback")) - // .def("onPluginEnabled", +[](IOrganizer* o, QString const& name, - // std::function const& func) { - // o->onPluginEnabled(name, func); - // }, (py::arg("name"), py::arg("callback"))) - // .def("onPluginDisabled", +[](IOrganizer* o, std::function const& func) { - // o->onPluginDisabled(func); - // }, py::arg("callback")) - // .def("onPluginDisabled", +[](IOrganizer* o, QString const& name, - // std::function const& func) { - // o->onPluginDisabled(name, func); - // }, (py::arg("name"), py::arg("callback"))) - - // // DEPRECATED: - // .def("getMod", +[](IOrganizer* o, QString const& name) { - // utils::show_deprecation_warning("getMod", - // "IOrganizer::getMod(str) is deprecated, use - // IModList::getMod(str) instead."); - // return o->modList()->getMod(name); - // }, py::return_value_policy(), - // py::arg("name")) .def("removeMod", +[](IOrganizer* o, IModInterface - // *mod) { - // utils::show_deprecation_warning("removeMod", - // "IOrganizer::removeMod(IModInterface) is deprecated, use - // IModList::removeMod(IModInterface) instead."); - // return o->modList()->removeMod(mod); - // }, py::arg("mod")) - // .def("modsSortedByProfilePriority", +[](IOrganizer* o) { - // utils::show_deprecation_warning("modsSortedByProfilePriority", - // "IOrganizer::modsSortedByProfilePriority() is deprecated, use - // IModList::allModsByProfilePriority() instead."); - // return o->modList()->allModsByProfilePriority(); - // }) - // .def("refreshModList", +[](IOrganizer* o, bool s) { - // utils::show_deprecation_warning("refreshModList", - // "IOrganizer::refreshModList(bool) is deprecated, use - // IOrganizer::refresh(bool) instead."); - // o->refresh(s); - // }, (py::arg("save_changes") = true)) - // .def("onModInstalled", +[](IOrganizer* organizer, const - // std::function& func) { - // utils::show_deprecation_warning("onModInstalled", - // "IOrganizer::onModInstalled(Callable[[str], None]) is deprecated, - // " "use IModList::onModInstalled(Callable[[IModInterface], None]) - // instead."); - // return - // organizer->modList()->onModInstalled([func](MOBase::IModInterface* - // m) { func(m->name()); });; - // }, py::arg("callback")) - - // .def("getPluginDataPath", &IOrganizer::getPluginDataPath) - // .staticmethod("getPluginDataPath") - - // ; - - mo2::python::add_ifiletree_bindings(m); - - py::class_(m, "IProfile") - .def("name", &IProfile::name) - .def("absolutePath", &IProfile::absolutePath) - .def("localSavesEnabled", &IProfile::localSavesEnabled) - .def("localSettingsEnabled", &IProfile::localSettingsEnabled) - .def("invalidationActive", - [](const IProfile* p) { - bool supported; - bool active = p->invalidationActive(&supported); - return py::make_tuple(active, supported); - }) - .def("absoluteIniFilePath", &IProfile::absoluteIniFilePath, - py::arg("inifile")); - - // py::class_("IModRepositoryBridge", py::no_init) - // .def("requestDescription", &IModRepositoryBridge::requestDescription, - // (py::arg("game_name"), "mod_id", "user_data")) .def("requestFiles", - // &IModRepositoryBridge::requestFiles, (py::arg("game_name"), "mod_id", - // "user_data")) .def("requestFileInfo", - // &IModRepositoryBridge::requestFileInfo, (py::arg("game_name"), - // "mod_id", "file_id", "user_data")) .def("requestDownloadURL", - // &IModRepositoryBridge::requestDownloadURL, (py::arg("game_name"), - // "mod_id", "file_id", "user_data")) .def("requestToggleEndorsement", - // &IModRepositoryBridge::requestToggleEndorsement, - // (py::arg("game_name"), "mod_id", "mod_version", "endorse", - // "user_data")) - - // Q_DELEGATE(IModRepositoryBridge, QObject, "_object") - // ; - - // py::class_("ModRepositoryFileInfo", py::no_init) - // .def(py::init(py::arg("other"))) - // .def(py::init>((py::arg("game_name"), - // "mod_id", "file_id"))) .def("__str__", - // &ModRepositoryFileInfo::toString) .def("createFromJson", - // &ModRepositoryFileInfo::createFromJson, - // py::arg("data")).staticmethod("createFromJson") - // .def_readwrite("name", &ModRepositoryFileInfo::name) - // .def_readwrite("uri", &ModRepositoryFileInfo::uri) - // .def_readwrite("description", &ModRepositoryFileInfo::description) - // .def_readwrite("version", &ModRepositoryFileInfo::version) - // .def_readwrite("newestVersion", - // &ModRepositoryFileInfo::newestVersion) .def_readwrite("categoryID", - // &ModRepositoryFileInfo::categoryID) .def_readwrite("modName", - // &ModRepositoryFileInfo::modName) .def_readwrite("gameName", - // &ModRepositoryFileInfo::gameName) .def_readwrite("modID", - // &ModRepositoryFileInfo::modID) .def_readwrite("fileID", - // &ModRepositoryFileInfo::fileID) .def_readwrite("fileSize", - // &ModRepositoryFileInfo::fileSize) .def_readwrite("fileName", - // &ModRepositoryFileInfo::fileName) .def_readwrite("fileCategory", - // &ModRepositoryFileInfo::fileCategory) .def_readwrite("fileTime", - // &ModRepositoryFileInfo::fileTime) .def_readwrite("repository", - // &ModRepositoryFileInfo::repository) .def_readwrite("userData", - // &ModRepositoryFileInfo::userData) - // ; - - // py::class_("IDownloadManager", - // py::no_init) - // .def("startDownloadURLs", &IDownloadManager::startDownloadURLs, - // py::arg("urls")) .def("startDownloadNexusFile", - // &IDownloadManager::startDownloadNexusFile, (py::arg("mod_id"), - // "file_id")) .def("downloadPath", &IDownloadManager::downloadPath, - // py::arg("id")) .def("onDownloadComplete", - // &IDownloadManager::onDownloadComplete, py::arg("callback")) - // .def("onDownloadPaused", &IDownloadManager::onDownloadPaused, - // py::arg("callback")) .def("onDownloadFailed", - // &IDownloadManager::onDownloadFailed, py::arg("callback")) - // .def("onDownloadRemoved", &IDownloadManager::onDownloadRemoved, - // py::arg("callback")) - // ; - - // py::class_("IInstallationManager", py::no_init) - // .def("getSupportedExtensions", - // &IInstallationManager::getSupportedExtensions) .def("extractFile", - // &IInstallationManager::extractFile, (py::arg("entry"), - // py::arg("silent") = false)) .def("extractFiles", - // &IInstallationManager::extractFiles, (py::arg("entries"), - // py::arg("silent") = false)) .def("createFile", - // +[](IInstallationManager* m, std::shared_ptr entry) { - // return m->createFile(utils::clean_shared_ptr(entry)); - // }, py::arg("entry")) - - // // accept both QString and GuessedValue since the conversion - // is not automatic in Python, and - // // return a tuple to get back the mod name and the mod ID - // .def("installArchive", +[](IInstallationManager* m, - // std::variant> modName, QString archive, - // int modId) { - // GuessedValue tmp; - // if (auto* p = std::get_if(&modName)) { - // tmp = *p; - // } - // else { - // tmp = std::get>(modName); - // } - // auto result = m->installArchive(tmp, archive, modId); - // return std::make_tuple(result, static_cast(tmp), modId); - // }, (py::arg("mod_name"), "archive", py::arg("mod_id") = 0)) - // ; - - py::enum_(m, "EndorsedState") - .value("ENDORSED_FALSE", EndorsedState::ENDORSED_FALSE) - .value("ENDORSED_TRUE", EndorsedState::ENDORSED_TRUE) - .value("ENDORSED_UNKNOWN", EndorsedState::ENDORSED_UNKNOWN) - .value("ENDORSED_NEVER", EndorsedState::ENDORSED_NEVER); - - py::enum_(m, "TrackedState") - .value("TRACKED_FALSE", TrackedState::TRACKED_FALSE) - .value("TRACKED_TRUE", TrackedState::TRACKED_TRUE) - .value("TRACKED_UNKNOWN", TrackedState::TRACKED_UNKNOWN); - - // py::class_("IModInterface", - // py::no_init) - // .def("name", &IModInterface::name) - // .def("absolutePath", &IModInterface::absolutePath) - - // .def("comments", &IModInterface::comments) - // .def("notes", &IModInterface::notes) - // .def("gameName", &IModInterface::gameName) - // .def("repository", &IModInterface::repository) - // .def("nexusId", &IModInterface::nexusId) - // .def("version", &IModInterface::version) - // .def("newestVersion", &IModInterface::newestVersion) - // .def("ignoredVersion", &IModInterface::ignoredVersion) - // .def("installationFile", &IModInterface::installationFile) - // .def("converted", &IModInterface::converted) - // .def("validated", &IModInterface::validated) - // .def("color", &IModInterface::color) - // .def("url", &IModInterface::url) - // .def("primaryCategory", &IModInterface::primaryCategory) - // .def("categories", &IModInterface::categories) - // .def("trackedState", &IModInterface::trackedState) - // .def("endorsedState", &IModInterface::endorsedState) - // .def("fileTree", &IModInterface::fileTree) - // .def("isOverwrite", &IModInterface::isOverwrite) - // .def("isBackup", &IModInterface::isBackup) - // .def("isSeparator", &IModInterface::isSeparator) - // .def("isForeign", &IModInterface::isForeign) - - // .def("setVersion", &IModInterface::setVersion, py::arg("version")) - // .def("setNewestVersion", &IModInterface::setNewestVersion, - // py::arg("version")) .def("setIsEndorsed", - // &IModInterface::setIsEndorsed, py::arg("endorsed")) - // .def("setNexusID", &IModInterface::setNexusID, py::arg("nexus_id")) - // .def("addNexusCategory", &IModInterface::addNexusCategory, - // py::arg("category_id")) .def("addCategory", - // &IModInterface::addCategory, py::arg("name")) .def("removeCategory", - // &IModInterface::removeCategory, py::arg("name")) .def("setGameName", - // &IModInterface::setGameName, py::arg("name")) .def("setUrl", - // &IModInterface::setUrl, py::arg("url")) .def("pluginSetting", - // &IModInterface::pluginSetting, (py::arg("plugin_name"), "key", - // py::arg("default") = QVariant())) .def("pluginSettings", - // &IModInterface::pluginSettings, py::arg("plugin_name")) - // .def("setPluginSetting", &IModInterface::setPluginSetting, - // (py::arg("plugin_name"), "key", py::arg("value"))) - // .def("clearPluginSettings", &IModInterface::clearPluginSettings, - // py::arg("plugin_name")) - - // ; - - py::enum_(m, "GuessQuality") - .value("INVALID", MOBase::GUESS_INVALID) - .value("FALLBACK", MOBase::GUESS_FALLBACK) - .value("GOOD", MOBase::GUESS_GOOD) - .value("META", MOBase::GUESS_META) - .value("PRESET", MOBase::GUESS_PRESET) - .value("USER", MOBase::GUESS_USER); - - // py::class_, - // boost::noncopyable>("GuessedString") - // .def(py::init<>()) - // .def(py::init((py::arg("value"), - // py::arg("quality") = EGuessQuality::GUESS_USER))) .def("update", - // static_cast& - // (GuessedValue::*)(const - // QString&)>(&GuessedValue::update), py::return_self<>(), - // py::arg("value")) - // .def("update", - // static_cast& - // (GuessedValue::*)(const QString&, - // EGuessQuality)>(&GuessedValue::update), - // py::return_self<>(), (py::arg("value"), "quality")) - - // // Methods to simulate the assignment operator: - // .def("reset", +[](GuessedValue* gv) { - // *gv = GuessedValue(); }, py::return_self<>()) - // .def("reset", +[](GuessedValue* gv, const QString& value, - // EGuessQuality eq) { - // *gv = GuessedValue(value, eq); }, py::return_self<>(), - // (py::arg("value"), "quality")) - // .def("reset", +[](GuessedValue* gv, const - // GuessedValue& other) { - // *gv = other; }, py::return_self<>(), py::arg("other")) - - // // Use an intermediate lambda to avoid having to register the - // std::function conversion: .def("setFilter", - // +[](GuessedValue* gv, std::function(QString const&)> fn) { - // gv->setFilter([fn](QString& s) { - // auto ret = fn(s); - // return std::visit([&s](auto v) { - // if constexpr (std::is_same_v) { - // s = v; - // return true; - // } - // else if constexpr (std::is_same_v) { - // return v; - // } - // }, ret); - // }); - // }, py::arg("filter")) - - // // This makes a copy in python but it more practical than exposing an - // iterator: .def("variants", &GuessedValue::variants, - // py::return_value_policy()) .def("__str__", - // &MOBase::GuessedValue::operator const QString&, - // py::return_value_policy()) - // ; - - py::enum_(m, "PluginState") - .value("missing", IPluginList::STATE_MISSING) - .value("inactive", IPluginList::STATE_INACTIVE) - .value("active", IPluginList::STATE_ACTIVE) - - .value("MISSING", IPluginList::STATE_MISSING) - .value("INACTIVE", IPluginList::STATE_INACTIVE) - .value("ACTIVE", IPluginList::STATE_ACTIVE); - m.def("testFlags1", [](IPluginList::PluginStates states) { std::vector res; if (states.testFlag(IPluginList::STATE_MISSING)) { @@ -804,504 +216,6 @@ PYBIND11_MODULE(mobase, m) } return res; }); - - // py::class_("IPluginList", py::no_init) - // .def("state", &MOBase::IPluginList::state, py::arg("name")) - // .def("priority", &MOBase::IPluginList::priority, py::arg("name")) - // .def("setPriority", &MOBase::IPluginList::setPriority, - // (py::arg("name"), "priority")) .def("loadOrder", - // &MOBase::IPluginList::loadOrder, py::arg("name")) .def("isMaster", - // &MOBase::IPluginList::isMaster, py::arg("name")) .def("masters", - // &MOBase::IPluginList::masters, py::arg("name")) .def("origin", - // &MOBase::IPluginList::origin, py::arg("name")) .def("onRefreshed", - // &MOBase::IPluginList::onRefreshed, py::arg("callback")) - // .def("onPluginMoved", &MOBase::IPluginList::onPluginMoved, - // py::arg("callback")) - - // // Kept but deprecated for backward compatibility: - // .def("onPluginStateChanged", +[](IPluginList* modList, const - // std::function& fn) { - // utils::show_deprecation_warning("onPluginStateChanged", - // "onPluginStateChanged(Callable[[str, IPluginList.PluginStates], - // None]) is deprecated, " "use - // onPluginStateChanged(Callable[[Dict[str, - // IPluginList.PluginStates], None]) instead."); - // return modList->onPluginStateChanged([fn](auto const& map) { - // for (const auto& entry : map) { - // fn(entry.first, entry.second); - // } - // }); - // }, py::arg("callback")) - // .def("onPluginStateChanged", - // &MOBase::IPluginList::onPluginStateChanged, py::arg("callback")) - // .def("pluginNames", &MOBase::IPluginList::pluginNames) - // .def("setState", &MOBase::IPluginList::setState, (py::arg("name"), - // "state")) .def("setLoadOrder", &MOBase::IPluginList::setLoadOrder, - // py::arg("loadorder")) - // ; - - // py::enum_("ModState") - // .value("exists", IModList::STATE_EXISTS) - // .value("active", IModList::STATE_ACTIVE) - // .value("essential", IModList::STATE_ESSENTIAL) - // .value("empty", IModList::STATE_EMPTY) - // .value("endorsed", IModList::STATE_ENDORSED) - // .value("valid", IModList::STATE_VALID) - // .value("alternate", IModList::STATE_ALTERNATE) - - // .value("EXISTS", IModList::STATE_EXISTS) - // .value("ACTIVE", IModList::STATE_ACTIVE) - // .value("ESSENTIAL", IModList::STATE_ESSENTIAL) - // .value("EMPTY", IModList::STATE_EMPTY) - // .value("ENDORSED", IModList::STATE_ENDORSED) - // .value("VALID", IModList::STATE_VALID) - // .value("ALTERNATE", IModList::STATE_ALTERNATE) - // ; - - // py::class_("IModList", py::no_init) - // .def("displayName", &MOBase::IModList::displayName, py::arg("name")) - // .def("allMods", &MOBase::IModList::allMods) - // .def("allModsByProfilePriority", - // &MOBase::IModList::allModsByProfilePriority, py::arg("profile") = - // py::ptr((IProfile*)nullptr)) - - // .def("getMod", &MOBase::IModList::getMod, - // py::return_value_policy(), - // py::arg("name")) .def("removeMod", &MOBase::IModList::removeMod, - // py::arg("mod")) .def("renameMod", &MOBase::IModList::renameMod, - // py::return_value_policy(), - // (py::arg("mod"), py::arg("name"))) - - // .def("state", &MOBase::IModList::state, py::arg("name")) - // .def("setActive", - // static_cast(&MOBase::IModList::setActive), (py::arg("names"), "active")) - // .def("setActive", - // static_cast(&MOBase::IModList::setActive), (py::arg("name"), "active")) - // .def("priority", &MOBase::IModList::priority, py::arg("name")) - // .def("setPriority", &MOBase::IModList::setPriority, (py::arg("name"), - // "priority")) - - // // Kept but deprecated for backward compatibility: - // .def("onModStateChanged", +[](IModList* modList, const - // std::function& fn) { - // utils::show_deprecation_warning("onModStateChanged", - // "onModStateChanged(Callable[[str, IModList.ModStates], None]) is - // deprecated, " "use onModStateChanged(Callable[[Dict[str, - // IModList.ModStates], None]) instead."); - // return modList->onModStateChanged([fn](auto const& map) { - // for (const auto& entry : map) { - // fn(entry.first, entry.second); - // } - // }); - // }, py::arg("callback")) - - // .def("onModInstalled", &MOBase::IModList::onModInstalled, - // py::arg("callback")) .def("onModRemoved", - // &MOBase::IModList::onModRemoved, py::arg("callback")) - // .def("onModStateChanged", &MOBase::IModList::onModStateChanged, - // py::arg("callback")) .def("onModMoved", - // &MOBase::IModList::onModMoved, py::arg("callback")) - // ; - - // // Note: localizedName, master, requirements and enabledByDefault have to - // go in all the plugin wrappers declaration, - // // since the default functions are specific to each wrapper, otherwise in - // turns into an - // // infinite recursion mess. - // py::class_("IPlugin") - // .def("init", py::pure_virtual(&MOBase::IPlugin::init), - // py::arg("organizer")) .def("name", - // py::pure_virtual(&MOBase::IPlugin::name)) .def("localizedName", - // &MOBase::IPlugin::localizedName, - // &IPluginWrapper::localizedName_Default) .def("master", - // &MOBase::IPlugin::master, &IPluginWrapper::master_Default) - // .def("author", py::pure_virtual(&MOBase::IPlugin::author)) - // .def("description", py::pure_virtual(&MOBase::IPlugin::description)) - // .def("version", py::pure_virtual(&MOBase::IPlugin::version)) - // .def("requirements", &MOBase::IPlugin::requirements, - // &IPluginWrapper::requirements_Default) .def("settings", - // py::pure_virtual(&MOBase::IPlugin::settings)) .def("enabledByDefault", - // &MOBase::IPlugin::enabledByDefault, - // &IPluginWrapper::enabledByDefault_Default) - // ; - - // py::class_, - // boost::noncopyable>("IPluginDiagnose") - // .def("localizedName", &MOBase::IPlugin::localizedName, - // &IPluginDiagnoseWrapper::localizedName_Default) .def("master", - // &MOBase::IPlugin::master, &IPluginDiagnoseWrapper::master_Default) - // .def("requirements", &MOBase::IPlugin::requirements, - // &IPluginDiagnoseWrapper::requirements_Default) - // .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, - // &IPluginDiagnoseWrapper::enabledByDefault_Default) - - // .def("activeProblems", - // py::pure_virtual(&MOBase::IPluginDiagnose::activeProblems)) - // .def("shortDescription", - // py::pure_virtual(&MOBase::IPluginDiagnose::shortDescription), - // py::arg("key")) .def("fullDescription", - // py::pure_virtual(&MOBase::IPluginDiagnose::fullDescription), - // py::arg("key")) .def("hasGuidedFix", - // py::pure_virtual(&MOBase::IPluginDiagnose::hasGuidedFix), - // py::arg("key")) .def("startGuidedFix", - // py::pure_virtual(&MOBase::IPluginDiagnose::startGuidedFix), - // py::arg("key")) .def("_invalidate", - // &IPluginDiagnoseWrapper::invalidate) - // ; - - // py::class_("Mapping", py::init<>()) - // .def("__init__", py::make_constructor(+[](QString src, QString dst, - // bool dir, bool crt) -> Mapping* { - // return new Mapping{ src, dst, dir, crt }; - // }, py::default_call_policies(), - // (py::arg("source"), py::arg("destination"), - // py::arg("is_directory"), py::arg("create_target") = false))) - // .def_readwrite("source", &Mapping::source) - // .def_readwrite("destination", &Mapping::destination) - // .def_readwrite("isDirectory", &Mapping::isDirectory) - // .def_readwrite("createTarget", &Mapping::createTarget) - // .def("__str__", +[](Mapping * m) { - // return fmt::format(L"Mapping({}, {}, {}, {})", - // m->source.toStdWString(), m->destination.toStdWString(), - // m->isDirectory, m->createTarget); - // }) - // ; - - // py::class_, - // boost::noncopyable>("IPluginFileMapper") - // .def("localizedName", &MOBase::IPlugin::localizedName, - // &IPluginFileMapperWrapper::localizedName_Default) .def("master", - // &MOBase::IPlugin::master, &IPluginFileMapperWrapper::master_Default) - // .def("requirements", &MOBase::IPlugin::requirements, - // &IPluginFileMapperWrapper::requirements_Default) - // .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, - // &IPluginFileMapperWrapper::enabledByDefault_Default) - - // .def("mappings", - // py::pure_virtual(&MOBase::IPluginFileMapper::mappings)) - // ; - - // py::enum_("LoadOrderMechanism") - // .value("FileTime", MOBase::IPluginGame::LoadOrderMechanism::FileTime) - // .value("PluginsTxt", - // MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt) - - // .value("FILE_TIME", - // MOBase::IPluginGame::LoadOrderMechanism::FileTime) - // .value("PLUGINS_TXT", - // MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt) - // ; - - // py::enum_("SortMechanism") - // .value("NONE", MOBase::IPluginGame::SortMechanism::NONE) - // .value("MLOX", MOBase::IPluginGame::SortMechanism::MLOX) - // .value("BOSS", MOBase::IPluginGame::SortMechanism::BOSS) - // .value("LOOT", MOBase::IPluginGame::SortMechanism::LOOT) - // ; - - // // This doesn't actually do the conversion, but might be convenient for - // accessing the names for enum bits - // py::enum_("ProfileSetting") - // .value("mods", MOBase::IPluginGame::MODS) - // .value("configuration", MOBase::IPluginGame::CONFIGURATION) - // .value("savegames", MOBase::IPluginGame::SAVEGAMES) - // .value("preferDefaults", MOBase::IPluginGame::PREFER_DEFAULTS) - - // .value("MODS", MOBase::IPluginGame::MODS) - // .value("CONFIGURATION", MOBase::IPluginGame::CONFIGURATION) - // .value("SAVEGAMES", MOBase::IPluginGame::SAVEGAMES) - // .value("PREFER_DEFAULTS", MOBase::IPluginGame::PREFER_DEFAULTS) - // ; - - // py::class_, - // boost::noncopyable>("IPluginGame") - // .def("localizedName", &MOBase::IPlugin::localizedName, - // &IPluginGameWrapper::localizedName_Default) .def("master", - // &MOBase::IPlugin::master, &IPluginGameWrapper::master_Default) - - // .def("detectGame", - // py::pure_virtual(&MOBase::IPluginGame::detectGame)) .def("gameName", - // py::pure_virtual(&MOBase::IPluginGame::gameName)) - // .def("initializeProfile", - // py::pure_virtual(&MOBase::IPluginGame::initializeProfile), - // (py::arg("directory"), "settings")) .def("listSaves", - // py::pure_virtual(&MOBase::IPluginGame::listSaves), py::arg("folder")) - // .def("isInstalled", - // py::pure_virtual(&MOBase::IPluginGame::isInstalled)) .def("gameIcon", - // py::pure_virtual(&MOBase::IPluginGame::gameIcon)) - // .def("gameDirectory", - // py::pure_virtual(&MOBase::IPluginGame::gameDirectory)) - // .def("dataDirectory", - // py::pure_virtual(&MOBase::IPluginGame::dataDirectory)) - // .def("setGamePath", - // py::pure_virtual(&MOBase::IPluginGame::setGamePath), py::arg("path")) - // .def("documentsDirectory", - // py::pure_virtual(&MOBase::IPluginGame::documentsDirectory)) - // .def("savesDirectory", - // py::pure_virtual(&MOBase::IPluginGame::savesDirectory)) - // .def("executables", - // py::pure_virtual(&MOBase::IPluginGame::executables)) - // .def("executableForcedLoads", - // py::pure_virtual(&MOBase::IPluginGame::executableForcedLoads)) - // .def("steamAPPId", - // py::pure_virtual(&MOBase::IPluginGame::steamAPPId)) - // .def("primaryPlugins", - // py::pure_virtual(&MOBase::IPluginGame::primaryPlugins)) - // .def("gameVariants", - // py::pure_virtual(&MOBase::IPluginGame::gameVariants)) - // .def("setGameVariant", - // py::pure_virtual(&MOBase::IPluginGame::setGameVariant), - // py::arg("variant")) .def("binaryName", - // py::pure_virtual(&MOBase::IPluginGame::binaryName)) - // .def("gameShortName", - // py::pure_virtual(&MOBase::IPluginGame::gameShortName)) - // .def("primarySources", - // py::pure_virtual(&MOBase::IPluginGame::primarySources)) - // .def("validShortNames", - // py::pure_virtual(&MOBase::IPluginGame::validShortNames)) - // .def("gameNexusName", - // py::pure_virtual(&MOBase::IPluginGame::gameNexusName)) - // .def("iniFiles", py::pure_virtual(&MOBase::IPluginGame::iniFiles)) - // .def("DLCPlugins", - // py::pure_virtual(&MOBase::IPluginGame::DLCPlugins)) .def("CCPlugins", - // py::pure_virtual(&MOBase::IPluginGame::CCPlugins)) - // .def("loadOrderMechanism", - // py::pure_virtual(&MOBase::IPluginGame::loadOrderMechanism)) - // .def("sortMechanism", - // py::pure_virtual(&MOBase::IPluginGame::sortMechanism)) - // .def("nexusModOrganizerID", - // py::pure_virtual(&MOBase::IPluginGame::nexusModOrganizerID)) - // .def("nexusGameID", - // py::pure_virtual(&MOBase::IPluginGame::nexusGameID)) - // .def("looksValid", - // py::pure_virtual(&MOBase::IPluginGame::looksValid), - // py::arg("directory")) .def("gameVersion", - // py::pure_virtual(&MOBase::IPluginGame::gameVersion)) - // .def("getLauncherName", - // py::pure_virtual(&MOBase::IPluginGame::getLauncherName)) - - // .def("featureList", +[](MOBase::IPluginGame* p) { - // // Constructing a dict from class name to actual object: - // py::dict dict; - // mp11::mp_for_each< - // // Must user pointers because mp_for_each construct object: - // mp11::mp_transform - // >([&](auto* pt) { - // using T = std::remove_pointer_t; - // typename py::reference_existing_object::apply::type - // converter; - - // // Retrieve the python class object: - // const py::converter::registration* registration = - // py::converter::registry::query(py::type_id()); py::object key - // = - // py::object(py::handle<>(py::borrowed(registration->get_class_object()))); - - // // Set the object: - // dict[key] = py::handle<>(converter(p->feature())); - // }); - // return dict; - // }) - - // .def("feature", +[](MOBase::IPluginGame* p, py::object clsObj) { - // py::object feature; - // mp11::mp_for_each< - // // Must user pointers because mp_for_each construct object: - // mp11::mp_transform - // >([&](auto* pt) { - // using T = std::remove_pointer_t; - // typename py::reference_existing_object::apply::type - // converter; - - // // Retrieve the python class object: - // const py::converter::registration* registration = - // py::converter::registry::query(py::type_id()); - - // if (clsObj.ptr() == (PyObject*) registration->get_class_object()) - // { - // feature = py::object(py::handle<>(converter(p->feature()))); - // } - // }); - // return feature; - // }, py::arg("feature_type")) - // ; - - // py::enum_("InstallResult") - // .value("SUCCESS", MOBase::IPluginInstaller::RESULT_SUCCESS) - // .value("FAILED", MOBase::IPluginInstaller::RESULT_FAILED) - // .value("CANCELED", MOBase::IPluginInstaller::RESULT_CANCELED) - // .value("MANUAL_REQUESTED", - // MOBase::IPluginInstaller::RESULT_MANUALREQUESTED) - // .value("NOT_ATTEMPTED", - // MOBase::IPluginInstaller::RESULT_NOTATTEMPTED) - // ; - - // py::class_, - // boost::noncopyable>("IPluginInstaller", py::no_init) - // .def("isArchiveSupported", &IPluginInstaller::isArchiveSupported, - // py::arg("tree")) .def("priority", &IPluginInstaller::priority) - // .def("onInstallationStart", &IPluginInstaller::onInstallationStart, - // (py::arg("archive"), py::arg("reinstallation"), - // py::arg("current_mod"))) .def("onInstallationEnd", - // &IPluginInstaller::onInstallationEnd, (py::arg("result"), - // py::arg("new_mod"))) .def("isManualInstaller", - // &IPluginInstaller::isManualInstaller) .def("setParentWidget", - // &IPluginInstaller::setParentWidget, py::arg("parent")) - // .def("setInstallationManager", - // &IPluginInstaller::setInstallationManager, py::arg("manager")) - // ; - - // py::class_, - // boost::noncopyable>("IPluginInstallerSimple") - // .def("onInstallationStart", &IPluginInstaller::onInstallationStart, - // (py::arg("archive"), py::arg("reinstallation"), - // py::arg("current_mod"))) .def("onInstallationEnd", - // &IPluginInstaller::onInstallationEnd, (py::arg("result"), - // py::arg("new_mod"))) .def("localizedName", - // &MOBase::IPlugin::localizedName, - // &IPluginInstallerSimpleWrapper::localizedName_Default) .def("master", - // &MOBase::IPlugin::master, - // &IPluginInstallerSimpleWrapper::master_Default) .def("requirements", - // &MOBase::IPlugin::requirements, - // &IPluginInstallerSimpleWrapper::requirements_Default) - // .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, - // &IPluginInstallerSimpleWrapper::enabledByDefault_Default) - - // // Note: Keeping the variant here even if we always return a tuple to - // be consistent with the wrapper and - // // have proper stubs generation. - // .def("install", +[](IPluginInstallerSimple* p, GuessedValue& - // modName, std::shared_ptr& tree, QString& version, int& - // nexusID) - // -> std::variant, - // std::tuple, QString, int>> { - // auto result = p->install(modName, tree, version, nexusID); - // return std::make_tuple(result, tree, version, nexusID); - // }, (py::arg("name"), "tree", "version", "nexus_id")) - // .def("_parentWidget", &IPluginInstallerSimpleWrapper::parentWidget, - // py::return_value_policy()) .def("_manager", - // &IPluginInstallerSimpleWrapper::manager, - // py::return_value_policy()) - // ; - - // py::class_, - // boost::noncopyable>("IPluginInstallerCustom") - // .def("onInstallationStart", &IPluginInstaller::onInstallationStart, - // (py::arg("archive"), py::arg("reinstallation"), - // py::arg("current_mod"))) .def("onInstallationEnd", - // &IPluginInstaller::onInstallationEnd, (py::arg("result"), - // py::arg("new_mod"))) .def("localizedName", - // &MOBase::IPlugin::localizedName, - // &IPluginInstallerCustomWrapper::localizedName_Default) .def("master", - // &MOBase::IPlugin::master, - // &IPluginInstallerCustomWrapper::master_Default) .def("requirements", - // &MOBase::IPlugin::requirements, - // &IPluginInstallerCustomWrapper::requirements_Default) - // .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, - // &IPluginInstallerCustomWrapper::enabledByDefault_Default) - - // // Needs to add both otherwize boost does not understand: - // .def("isArchiveSupported", &IPluginInstaller::isArchiveSupported, - // py::arg("tree")) .def("isArchiveSupported", - // &IPluginInstallerCustom::isArchiveSupported, py::arg("archive_name")) - // .def("supportedExtensions", - // &IPluginInstallerCustom::supportedExtensions) .def("install", - // &IPluginInstallerCustom::install, (py::arg("mod_name"), "game_name", - // "archive_name", "version", "nexus_id")) .def("_parentWidget", - // &IPluginInstallerSimpleWrapper::parentWidget, - // py::return_value_policy()) .def("_manager", - // &IPluginInstallerCustomWrapper::manager, - // py::return_value_policy()) - // ; - - // py::class_, - // boost::noncopyable>("IPluginModPage") - // .def("localizedName", &MOBase::IPlugin::localizedName, - // &IPluginModPageWrapper::localizedName_Default) .def("master", - // &MOBase::IPlugin::master, &IPluginModPageWrapper::master_Default) - // .def("requirements", &MOBase::IPlugin::requirements, - // &IPluginModPageWrapper::requirements_Default) .def("enabledByDefault", - // &MOBase::IPlugin::enabledByDefault, - // &IPluginModPageWrapper::enabledByDefault_Default) - - // .def("displayName", py::pure_virtual(&IPluginModPage::displayName)) - // .def("icon", py::pure_virtual(&IPluginModPage::icon)) - // .def("pageURL", py::pure_virtual(&IPluginModPage::pageURL)) - // .def("useIntegratedBrowser", - // py::pure_virtual(&IPluginModPage::useIntegratedBrowser)) - // .def("handlesDownload", - // py::pure_virtual(&IPluginModPage::handlesDownload), - // (py::arg("page_url"), "download_url", "fileinfo")) - // .def("setParentWidget", &IPluginModPage::setParentWidget, - // &IPluginModPageWrapper::setParentWidget_Default, py::arg("parent")) - // .def("_parentWidget", &IPluginModPageWrapper::parentWidget, - // py::return_value_policy()) - // ; - - // py::class_, - // boost::noncopyable>("IPluginPreview") - // .def("localizedName", &MOBase::IPlugin::localizedName, - // &IPluginPreviewWrapper::localizedName_Default) .def("master", - // &MOBase::IPlugin::master, &IPluginPreviewWrapper::master_Default) - // .def("requirements", &MOBase::IPlugin::requirements, - // &IPluginPreviewWrapper::requirements_Default) .def("enabledByDefault", - // &MOBase::IPlugin::enabledByDefault, - // &IPluginPreviewWrapper::enabledByDefault_Default) - - // .def("supportedExtensions", - // py::pure_virtual(&IPluginPreview::supportedExtensions)) - // .def("genFilePreview", - // py::pure_virtual(&IPluginPreview::genFilePreview), - // py::return_value_policy(), - // (py::arg("filename"), "max_size")) - // ; - - // py::class_, - // boost::noncopyable>("IPluginTool") - // .def("localizedName", &MOBase::IPlugin::localizedName, - // &IPluginToolWrapper::localizedName_Default) .def("master", - // &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default) - // .def("requirements", &MOBase::IPlugin::requirements, - // &IPluginToolWrapper::requirements_Default) .def("enabledByDefault", - // &MOBase::IPlugin::enabledByDefault, - // &IPluginToolWrapper::enabledByDefault_Default) - - // .def("displayName", py::pure_virtual(&IPluginTool::displayName)) - // .def("tooltip", py::pure_virtual(&IPluginTool::tooltip)) - // .def("icon", py::pure_virtual(&IPluginTool::icon)) - // .def("display", py::pure_virtual(&IPluginTool::display)) - // .def("setParentWidget", &IPluginTool::setParentWidget, - // &IPluginToolWrapper::setParentWidget_Default, py::arg("parent")) - // .def("_parentWidget", &IPluginToolWrapper::parentWidget, - // py::return_value_policy()) - // ; - - // registerGameFeaturesPythonConverters(); - - m.def("getFileVersion", &MOBase::getFileVersion, py::arg("filepath")); - m.def("getProductVersion", &MOBase::getProductVersion, - py::arg("executable")); - m.def("getIconForExecutable", &MOBase::iconForExecutable, - py::arg("executable")); - - // py::object widgets(py::borrowed(PyImport_AddModule("mobase.widgets"))); - // py::scope().attr("widgets") = widgets; - // { - // py::scope w_ = widgets; - // register_widgets(); - // } - - // Expose MoVariant: MoVariant is a fake object whose only purpose is to be - // used as a type-hint on the python side (e.g., def foo(x: - // mobase.MoVariant)). The real MoVariant is defined in the generated stubs, - // since it's only relevant when doing type-checking, but this needs to be - // defined, otherwise MoVariant is not found when actually running plugins - // through MO2, making them crash. - m.attr("MoVariant") = py::none(); } /** @@ -1431,10 +345,10 @@ bool PythonRunner::initPython() void PythonRunner::initPath() { - static QStringList paths = { - QCoreApplication::applicationDirPath() + "/pythoncore.zip", - QCoreApplication::applicationDirPath() + "/pythoncore", - IOrganizer::getPluginDataPath()}; + static QStringList paths = {QCoreApplication::applicationDirPath() + + "/pythoncore.zip", + QCoreApplication::applicationDirPath() + "/pythoncore", + IOrganizer::getPluginDataPath()}; Py_SetPath(paths.join(';').toStdWString().c_str()); } @@ -1453,8 +367,7 @@ void PythonRunner::ensureFolderInPath(QString folder) } template -void PythonRunner::appendIfInstance(py::object const& obj, - QList& interfaces) +void PythonRunner::appendIfInstance(py::object const& obj, QList& interfaces) { if (py::isinstance(obj)) { interfaces.append(obj.cast()); @@ -1495,8 +408,7 @@ QList PythonRunner::load(const QString& identifier) QStringList parts = identifier.split("/"); std::string moduleName = ToString(parts.takeLast()); ensureFolderInPath(parts.join("/")); - moduleDict = - py::module_::import(moduleName.c_str()).attr("__dict__"); + moduleDict = py::module_::import(moduleName.c_str()).attr("__dict__"); } if (py::len(moduleDict) == 0) { @@ -1516,8 +428,8 @@ QList PythonRunner::load(const QString& identifier) else if (moduleDict.contains("createPlugins")) { py::object pyPlugins = moduleDict["createPlugins"](); if (!PySequence_Check(pyPlugins.ptr())) { - MOBase::log::error( - "Plugin {}: createPlugins must return a list.", identifier); + MOBase::log::error("Plugin {}: createPlugins must return a list.", + identifier); } else { py::list pyList(pyPlugins); @@ -1571,8 +483,8 @@ QList PythonRunner::load(const QString& identifier) } if (interfaceList.isEmpty()) { - MOBase::log::error( - "Plugin {}: no plugin interface implemented.", identifier); + MOBase::log::error("Plugin {}: no plugin interface implemented.", + identifier); } // Append the plugins to the main list: @@ -1614,8 +526,7 @@ void PythonRunner::unload(const QString& identifier) // If the path is under identifier, we need to unload // it. log::debug("Unloading module {} from {} for {}.", - keys[i].cast(), mpath, - identifier); + keys[i].cast(), mpath, identifier); PyDict_DelItem(modules.ptr(), keys[i].ptr()); } @@ -1630,8 +541,7 @@ void PythonRunner::unload(const QString& identifier) obj.attr("__dict__").attr("clear")(); } - log::debug("Deleting {} python objects for {}.", it->second.size(), - identifier); + log::debug("Deleting {} python objects for {}.", it->second.size(), identifier); m_PythonObjects.erase(it); } } diff --git a/src/runner-pybind11/pythonrunner.h b/src/runner-pybind11/pythonrunner.h index b4f4ed9..5be80c7 100644 --- a/src/runner-pybind11/pythonrunner.h +++ b/src/runner-pybind11/pythonrunner.h @@ -1,33 +1,28 @@ #ifndef PYTHONRUNNER_H #define PYTHONRUNNER_H -#include #include -#include +#include #include #include - +#include class IPythonRunner { public: + virtual QList load(const QString& identifier) = 0; + virtual void unload(const QString& identifier) = 0; - virtual QList load(const QString& identifier) = 0; - virtual void unload(const QString& identifier) = 0; + virtual bool isPythonInitialized() const = 0; - virtual bool isPythonInitialized() const = 0; - - virtual ~IPythonRunner() { } + virtual ~IPythonRunner() {} }; - #ifdef PYTHONRUNNER_LIBRARY #define PYDLLEXPORT Q_DECL_EXPORT -#else // PYTHONRUNNER_LIBRARY +#else // PYTHONRUNNER_LIBRARY #define PYDLLEXPORT Q_DECL_IMPORT -#endif // PYTHONRUNNER_LIBRARY +#endif // PYTHONRUNNER_LIBRARY -extern "C" PYDLLEXPORT IPythonRunner *CreatePythonRunner(); +extern "C" PYDLLEXPORT IPythonRunner* CreatePythonRunner(); - - -#endif // PYTHONRUNNER_H +#endif // PYTHONRUNNER_H diff --git a/src/runner-pybind11/pythonutils.cpp b/src/runner-pybind11/pythonutils.cpp index a393bf9..99f3450 100644 --- a/src/runner-pybind11/pythonutils.cpp +++ b/src/runner-pybind11/pythonutils.cpp @@ -26,8 +26,8 @@ namespace mo2::python { buffer_ << message; if (buffer_.tellp() != 0 && buffer_.str().back() == '\n') { const auto full_message = buffer_.str(); - MOBase::log::log( - level_, full_message.substr(0, full_message.length() - 1)); + MOBase::log::log(level_, + full_message.substr(0, full_message.length() - 1)); buffer_ = std::stringstream{}; } } @@ -39,8 +39,7 @@ namespace mo2::python { */ template pybind11::object make_python_type(std::string_view name, - pybind11::tuple base_classes, - Args&&... args) + pybind11::tuple base_classes, Args&&... args) { // this is ugly but that's how it's done in C Python auto type = py::reinterpret_borrow((PyObject*)&PyType_Type); @@ -89,9 +88,8 @@ namespace mo2::python { // There are other parameters that could be used, but this is minimal // for now (filename, line number, etc.). - const int level = record.attr("levelno").cast(); - const std::wstring msg = - py::str(record.attr("msg")).cast(); + const int level = record.attr("levelno").cast(); + const std::wstring msg = py::str(record.attr("msg")).cast(); switch (level) { case PyLogLevel::CRITICAL: @@ -132,8 +130,7 @@ namespace mo2::python { // create the default logger auto handler = MO2Handler(); handler.attr("setLevel")(PyLogLevel::DEBUG); - auto logger = - logging.attr("getLogger")(py::object(mobase.attr("__name__"))); + auto logger = logging.attr("getLogger")(py::object(mobase.attr("__name__"))); logger.attr("setLevel")(PyLogLevel::DEBUG); logger.attr("addHandler")(handler); @@ -142,8 +139,8 @@ namespace mo2::python { mobase.attr("logger") = logger; } - void show_deprecation_warning(std::string_view name, - std::string_view message, bool show_once) + void show_deprecation_warning(std::string_view name, std::string_view message, + bool show_once) { // Contains the list of filename / line number for which a deprecation @@ -151,10 +148,9 @@ namespace mo2::python { static std::set> DeprecatedLines; // Find the caller: - auto inspect = py::module_::import("inspect"); - auto current_frame = inspect.attr("currentframe")(); - py::sequence callable_frame = - inspect.attr("getouterframes")(current_frame, 2); + auto inspect = py::module_::import("inspect"); + auto current_frame = inspect.attr("currentframe")(); + py::sequence callable_frame = inspect.attr("getouterframes")(current_frame, 2); auto filename = callable_frame[-1].attr("filename").cast(); auto function = callable_frame[-1].attr("function").cast(); auto lineno = callable_frame[-1].attr("lineno").cast(); @@ -167,9 +163,8 @@ namespace mo2::python { // Register the deprecation: DeprecatedLines.emplace(filename, lineno); - auto path = - relative(std::filesystem::path(filename), - QCoreApplication::applicationDirPath().toStdWString()); + auto path = relative(std::filesystem::path(filename), + QCoreApplication::applicationDirPath().toStdWString()); // Show the message: if (message.empty()) { @@ -177,8 +172,8 @@ namespace mo2::python { path.native(), lineno); } else { - MOBase::log::warn("[deprecated] {} in {} [{}:{}]: {}", name, - function, path.native(), lineno, message); + MOBase::log::warn("[deprecated] {} in {} [{}:{}]: {}", name, function, + path.native(), lineno, message); } } diff --git a/src/runner-pybind11/pythonutils.h b/src/runner-pybind11/pythonutils.h index 84286ff..8e6cb76 100644 --- a/src/runner-pybind11/pythonutils.h +++ b/src/runner-pybind11/pythonutils.h @@ -32,9 +32,8 @@ namespace mo2::python { * @param message Deprecation message. * @param show_once Only show the message once per call location. */ - void show_deprecation_warning(std::string_view name, - std::string_view message = "", - bool show_once = true); + void show_deprecation_warning(std::string_view name, std::string_view message = "", + bool show_once = true); } // namespace mo2::python diff --git a/src/runner-pybind11/pythonwrapperutilities.h b/src/runner-pybind11/pythonwrapperutilities.h index 5da5f7d..bcd42de 100644 --- a/src/runner-pybind11/pythonwrapperutilities.h +++ b/src/runner-pybind11/pythonwrapperutilities.h @@ -18,10 +18,9 @@ namespace details { * @brief Common stuffs for all basicWrapperFunction methods. */ template - ReturnType - wrapperFunctionImplementation(WrapperTypePtr wrapper, bool apiTransfer, - Fn fn, boost::python::object* objPtr, - const char* methodName, Args... args) + ReturnType wrapperFunctionImplementation(WrapperTypePtr wrapper, bool apiTransfer, + Fn fn, boost::python::object* objPtr, + const char* methodName, Args... args) { boost::python::override implementation = [&]() { GILock lock; @@ -29,8 +28,7 @@ namespace details { }(); if (!implementation) { if constexpr (std::is_same_v) { - throw pyexcept::MissingImplementation(wrapper->className, - methodName); + throw pyexcept::MissingImplementation(wrapper->className, methodName); } else { return std::invoke(fn, wrapper, args...); @@ -79,8 +77,7 @@ namespace details { */ template ReturnType basicWrapperFunctionImplementation(const WrapperType* wrapper, - const char* methodName, - Args... args) + const char* methodName, Args... args) { return details::wrapperFunctionImplementation( wrapper, false, nullptr, nullptr, methodName, args...); @@ -108,8 +105,7 @@ ReturnType basicWrapperFunctionImplementation(const WrapperType* wrapper, template ReturnType basicWrapperFunctionImplementation(const WrapperType* wrapper, boost::python::object& ref, - const char* methodName, - Args... args) + const char* methodName, Args... args) { return details::wrapperFunctionImplementation( wrapper, false, nullptr, &ref, methodName, args...); @@ -133,8 +129,9 @@ ReturnType basicWrapperFunctionImplementation(const WrapperType* wrapper, * @throw pyexecpt::UnknownException if an unknown error occurs. */ template -ReturnType wrapperFunctionImplementationWithApiTransfer( - const WrapperType* wrapper, const char* methodName, Args... args) +ReturnType wrapperFunctionImplementationWithApiTransfer(const WrapperType* wrapper, + const char* methodName, + Args... args) { return details::wrapperFunctionImplementation( wrapper, true, nullptr, nullptr, methodName, args...); @@ -161,8 +158,7 @@ ReturnType wrapperFunctionImplementationWithApiTransfer( * @throw pyexecpt::UnknownException if an unknown error occurs. */ template -ReturnType basicWrapperFunctionImplementationWithDefault(WrapperTypePtr wrapper, - Fn fn, +ReturnType basicWrapperFunctionImplementationWithDefault(WrapperTypePtr wrapper, Fn fn, const char* methodName, Args... args) { @@ -194,12 +190,13 @@ ReturnType basicWrapperFunctionImplementationWithDefault(WrapperTypePtr wrapper, * @throw pyexecpt::UnknownException if an unknown error occurs. */ template -ReturnType basicWrapperFunctionImplementationWithDefault( - const WrapperType* wrapper, Fn fn, boost::python::object& ref, - const char* methodName, Args... args) +ReturnType +basicWrapperFunctionImplementationWithDefault(const WrapperType* wrapper, Fn fn, + boost::python::object& ref, + const char* methodName, Args... args) { - return details::wrapperFunctionImplementation( - wrapper, false, fn, &ref, methodName, args...); + return details::wrapperFunctionImplementation(wrapper, false, fn, &ref, + methodName, args...); } #endif // PYTHONWRAPPERUTILITIES_H diff --git a/src/runner-pybind11/shared_ptr_converter.h b/src/runner-pybind11/shared_ptr_converter.h index ab80ac3..41f6857 100644 --- a/src/runner-pybind11/shared_ptr_converter.h +++ b/src/runner-pybind11/shared_ptr_converter.h @@ -8,118 +8,132 @@ namespace utils { - // Shared pointers are handled in a special way by Boost.Python since they hold - // the wrapped Python object and only release it when the ref counter of the shared - // ptr drops to 0 using shared_ptr_deleter. - // - // Unfortunately for us, this will happen outside of the Python proxy for some objects - // and thus without the GIL lock, making everything crash, so we need a custom deleter - // that holds the GIL while releasing the lock. - // - // Note that this is only useful for Python -> C++ conversion, and without this, Boost - // will automatically wrapped the pointer. The C++ -> Python conversion is handled - // separately by boost::python::register_ptr_to_python. - // - // This is an open Boost.Python problem: https://github.com/boostorg/python/pull/11 + // Shared pointers are handled in a special way by Boost.Python since they hold + // the wrapped Python object and only release it when the ref counter of the shared + // ptr drops to 0 using shared_ptr_deleter. + // + // Unfortunately for us, this will happen outside of the Python proxy for some + // objects and thus without the GIL lock, making everything crash, so we need a + // custom deleter that holds the GIL while releasing the lock. + // + // Note that this is only useful for Python -> C++ conversion, and without this, + // Boost will automatically wrapped the pointer. The C++ -> Python conversion is + // handled separately by boost::python::register_ptr_to_python. + // + // This is an open Boost.Python problem: https://github.com/boostorg/python/pull/11 - template - struct shared_ptr_from_python; + template + struct shared_ptr_from_python; - namespace details { + namespace details { - struct shared_ptr_deleter_with_gil_lock : boost::python::converter::shared_ptr_deleter { + struct shared_ptr_deleter_with_gil_lock + : boost::python::converter::shared_ptr_deleter { - using shared_ptr_deleter::shared_ptr_deleter; + using shared_ptr_deleter::shared_ptr_deleter; - void operator()(void const* o) { - GILock lock; - shared_ptr_deleter::operator()(o); - } + void operator()(void const* o) + { + GILock lock; + shared_ptr_deleter::operator()(o); + } + }; + template + struct shared_ptr_void; + + template + struct shared_ptr_void> { + using type = std::shared_ptr; + }; + + template + struct shared_ptr_void> { + using type = boost::shared_ptr; + }; + + template + using shared_ptr_void_t = typename shared_ptr_void::type; + + } // namespace details + + template + struct shared_ptr_from_python { + using T = typename SharedPtr::element_type; + + shared_ptr_from_python() + { + using namespace boost::python; + converter::registry::insert( + &convertible, &construct, type_id() +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + , + &converter::expected_from_python_type_direct::get_pytype +#endif + ); + } + + private: + static void* convertible(PyObject* p) + { + if (p == Py_None) + return p; + + return boost::python::converter::get_lvalue_from_python( + p, boost::python::converter::registered::converters); + } + + static void + construct(PyObject* source, + boost::python::converter::rvalue_from_python_stage1_data* data) + { + using namespace boost::python; + void* const storage = + ((converter::rvalue_from_python_storage*)data) + ->storage.bytes; + // Deal with the "None" case. + if (data->convertible == source) + new (storage) SharedPtr(); + else { + details::shared_ptr_void_t hold_convertible_ref_count( + (void*)0, details::shared_ptr_deleter_with_gil_lock( + handle<>(borrowed(source)))); + // use aliasing constructor + new (storage) SharedPtr(hold_convertible_ref_count, + static_cast(data->convertible)); + } + + data->convertible = storage; + } }; + // release the bpy::object associated with the deleter of the given shared_ptr, + // if the given shared_ptr has a Boost.Python deleter + // + // this should only be used when returning from Python objects that have been + // created on the C++ side, e.g. if IFileTree.createOrphanTree() from Python and + // then return the tree + // + // for reason yet to be known, Boost.Python had a custom deleter in this case that + // tries to delete the bpy::object and fails, so we have to release the object + // manually + // template - struct shared_ptr_void; - - template - struct shared_ptr_void> { using type = std::shared_ptr; }; - - template - struct shared_ptr_void> { using type = boost::shared_ptr; }; - - template - using shared_ptr_void_t = typename shared_ptr_void::type; - - } - - template - struct shared_ptr_from_python - { - using T = typename SharedPtr::element_type; - - shared_ptr_from_python() + SharedPtr clean_shared_ptr(SharedPtr&& ptr) { - using namespace boost::python; - converter::registry::insert(&convertible, &construct, type_id() -#ifndef BOOST_PYTHON_NO_PY_SIGNATURES - , &converter::expected_from_python_type_direct::get_pytype -#endif - ); + if (auto* d = get_deleter(ptr); + d != nullptr) { + // we cannot do a proper reset() here, even with the GIL lock, for unknown + // reason, so we only release + // + // this might create lost references to Python object but this should not + // happen too often so hopefully it's not a big issue + // + d->owner.release(); + } + return ptr; } - private: - static void* convertible(PyObject* p) - { - if (p == Py_None) - return p; - - return boost::python::converter::get_lvalue_from_python(p, boost::python::converter::registered::converters); - } - - static void construct(PyObject* source, boost::python::converter::rvalue_from_python_stage1_data* data) - { - using namespace boost::python; - void* const storage = ((converter::rvalue_from_python_storage*)data)->storage.bytes; - // Deal with the "None" case. - if (data->convertible == source) - new (storage) SharedPtr(); - else - { - details::shared_ptr_void_t hold_convertible_ref_count( - (void*)0, details::shared_ptr_deleter_with_gil_lock(handle<>(borrowed(source)))); - // use aliasing constructor - new (storage) SharedPtr(hold_convertible_ref_count, - static_cast(data->convertible)); - } - - data->convertible = storage; - } - }; - - // release the bpy::object associated with the deleter of the given shared_ptr, - // if the given shared_ptr has a Boost.Python deleter - // - // this should only be used when returning from Python objects that have been created - // on the C++ side, e.g. if IFileTree.createOrphanTree() from Python and then return - // the tree - // - // for reason yet to be known, Boost.Python had a custom deleter in this case that tries - // to delete the bpy::object and fails, so we have to release the object manually - // - template - SharedPtr clean_shared_ptr(SharedPtr&& ptr) { - if (auto* d = get_deleter(ptr); d != nullptr) { - // we cannot do a proper reset() here, even with the GIL lock, for unknown reason, - // so we only release - // - // this might create lost references to Python object but this should not happen - // too often so hopefully it's not a big issue - // - d->owner.release(); - } - return ptr; - } - -} +} // namespace utils #endif \ No newline at end of file diff --git a/src/runner-pybind11/uibasewrappers.h b/src/runner-pybind11/uibasewrappers.h deleted file mode 100644 index 95e878a..0000000 --- a/src/runner-pybind11/uibasewrappers.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef UIBASEWRAPPERS_H -#define UIBASEWRAPPERS_H - - -#ifndef Q_MOC_RUN -#pragma warning (push, 0) -#include -#pragma warning (pop) -#endif - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "error.h" -#include "gilock.h" -#include "pythonwrapperutilities.h" - -// This can be extended in C++, so why not in Python: -class IPluginRequirementWrapper : public MOBase::IPluginRequirement, public boost::python::wrapper -{ -public: - static constexpr const char* className = "IPluginRequirement"; - using boost::python::wrapper::get_override; - - virtual std::optional check(MOBase::IOrganizer *o) const override { - return basicWrapperFunctionImplementation>(this, "check", boost::python::ptr(o)); - }; -}; - -// This needs to be extendable in Python, so actually needs a wrapper: -class ISaveGameWrapper : public MOBase::ISaveGame, public boost::python::wrapper -{ -public: - static constexpr const char* className = "ISaveGameWrapper"; - using boost::python::wrapper::get_override; - - virtual QString getFilepath() const override { return basicWrapperFunctionImplementation(this, "getFilepath"); }; - virtual QDateTime getCreationTime() const override { return basicWrapperFunctionImplementation(this, "getCreationTime"); }; - virtual QString getName() const override { return basicWrapperFunctionImplementation(this, "getName"); }; - virtual QString getSaveGroupIdentifier() const override { return basicWrapperFunctionImplementation(this, "getSaveGroupIdentifier"); }; - virtual QStringList allFiles() const override { return basicWrapperFunctionImplementation(this, "allFiles"); }; - -protected: - - friend class IPluginGameWrapper; -}; - -// This needs a wrapper but currently I have no idea how to expose this properly to python: -class ISaveGameInfoWidgetWrapper : public MOBase::ISaveGameInfoWidget, public boost::python::wrapper -{ -public: - static constexpr const char* className = "ISaveGameInfoWidgetWrapper"; - using boost::python::wrapper::get_override; - - // Bring the constructor: - using ISaveGameInfoWidget::ISaveGameInfoWidget; - - virtual void setSave(MOBase::ISaveGame const& save) override { basicWrapperFunctionImplementation(this, "setSave", boost::ref(save)); }; -}; - -#endif // UIBASEWRAPPERS_H diff --git a/src/runner-pybind11/widgets.cpp b/src/runner-pybind11/widgets.cpp deleted file mode 100644 index 44fe17c..0000000 --- a/src/runner-pybind11/widgets.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "widgets.h" - -#include - -#include - -#include "report.h" -// #include "variant_helper.h" - -namespace bpy = boost::python; - -using namespace MOBase; - -void register_widgets() { - - // TaskDialog is also in Windows System. - using TaskDialog = MOBase::TaskDialog; - - // TaskDialog - bpy::class_("TaskDialogButton", bpy::no_init) - .def(bpy::init((bpy::arg("text"), bpy::arg("description"), bpy::arg("button")))) - .def(bpy::init((bpy::arg("text"), bpy::arg("button")))); - - bpy::class_("TaskDialog", bpy::no_init) - .def("__init__", bpy::make_constructor(+[]( - QWidget* parent, QString const& title, QString const& main, - QString const& content, QString const& details, QMessageBox::Icon icon, - std::vector const& buttons, - std::variant> const& remember) { - auto* dialog = new TaskDialog(parent, title); - dialog->main(main).content(content).details(details).icon(icon); - - for (auto& button : buttons) { - dialog->button(button); - } - - std::visit([dialog](auto const& item) { - QString action, file; - if constexpr (std::is_same_v, QString>) { - action = item; - } - else { - action = std::get<0>(item); - file = std::get<1>(item); - } - dialog->remember(action, file); - }, remember); - - return dialog; - }, bpy::default_call_policies(), - (bpy::arg("parent") = (QWidget*)nullptr, bpy::arg("title") = "", bpy::arg("main") = "", bpy::arg("content") = "", - bpy::arg("details") = "", bpy::arg("icon") = QMessageBox::NoIcon, bpy::arg("buttons") = std::vector{}, - bpy::arg("remember") = ""))) - .def("setTitle", &TaskDialog::title, bpy::return_self<>(), bpy::arg("title")) - .def("setMain", &TaskDialog::main, bpy::return_self<>(), bpy::arg("main")) - .def("setContent", &TaskDialog::content, bpy::return_self<>(), bpy::arg("content")) - .def("setDetails", &TaskDialog::details, bpy::return_self<>(), bpy::arg("details")) - .def("setIcon", &TaskDialog::icon, bpy::return_self<>(), bpy::arg("icon")) - .def("addButton", &TaskDialog::button, bpy::return_self<>(), bpy::arg("button")) - .def("setRemember", &TaskDialog::remember, bpy::return_self<>(), (bpy::arg("action"), bpy::arg("file") = "")) - .def("setWidth", &TaskDialog::setWidth, bpy::arg("widget")) - .def("addContent", &TaskDialog::addContent, bpy::arg("widget")) - .def("exec", &TaskDialog::exec) - ; - -} diff --git a/src/runner-pybind11/widgets.h b/src/runner-pybind11/widgets.h deleted file mode 100644 index 9227d3d..0000000 --- a/src/runner-pybind11/widgets.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef PYTHON_WIDGETS_HPP -#define PYTHON_WIDGETS_HPP - -/** - * @brief Register uibase widgets in the current scope. - */ -void register_widgets(); - -#endif \ No newline at end of file diff --git a/src/runner-pybind11/wrappers/basic_classes.cpp b/src/runner-pybind11/wrappers/basic_classes.cpp new file mode 100644 index 0000000..6f1e12f --- /dev/null +++ b/src/runner-pybind11/wrappers/basic_classes.cpp @@ -0,0 +1,759 @@ +#include "wrappers.h" + +#include +#include +#include +#include + +#include "../pybind11_qt/pybind11_qt.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pyfiletree.h" + +using namespace MOBase; + +namespace mo2::python { + + namespace py = pybind11; + + void add_versioninfo_classes(py::module_ m) + { + py::enum_(m, "ReleaseType") + .value("final", MOBase::VersionInfo::RELEASE_FINAL) + .value("candidate", MOBase::VersionInfo::RELEASE_CANDIDATE) + .value("beta", MOBase::VersionInfo::RELEASE_BETA) + .value("alpha", MOBase::VersionInfo::RELEASE_ALPHA) + .value("prealpha", MOBase::VersionInfo::RELEASE_PREALPHA) + + .value("FINAL", MOBase::VersionInfo::RELEASE_FINAL) + .value("CANDIDATE", MOBase::VersionInfo::RELEASE_CANDIDATE) + .value("BETA", MOBase::VersionInfo::RELEASE_BETA) + .value("ALPHA", MOBase::VersionInfo::RELEASE_ALPHA) + .value("PRE_ALPHA", MOBase::VersionInfo::RELEASE_PREALPHA); + + py::enum_(m, "VersionScheme") + .value("discover", MOBase::VersionInfo::SCHEME_DISCOVER) + .value("regular", MOBase::VersionInfo::SCHEME_REGULAR) + .value("decimalmark", MOBase::VersionInfo::SCHEME_DECIMALMARK) + .value("numbersandletters", MOBase::VersionInfo::SCHEME_NUMBERSANDLETTERS) + .value("date", MOBase::VersionInfo::SCHEME_DATE) + .value("literal", MOBase::VersionInfo::SCHEME_LITERAL) + + .value("DISCOVER", MOBase::VersionInfo::SCHEME_DISCOVER) + .value("REGULAR", MOBase::VersionInfo::SCHEME_REGULAR) + .value("DECIMAL_MARK", MOBase::VersionInfo::SCHEME_DECIMALMARK) + .value("NUMBERS_AND_LETTERS", MOBase::VersionInfo::SCHEME_NUMBERSANDLETTERS) + .value("DATE", MOBase::VersionInfo::SCHEME_DATE) + .value("LITERAL", MOBase::VersionInfo::SCHEME_LITERAL); + + py::class_(m, "VersionInfo") + .def(py::init(), py::arg("value"), + py::arg("scheme") = VersionInfo::SCHEME_DISCOVER) + // note: order of the two init<> below is important because + // ReleaseType is a simple enum with an implicit int conversion. + .def(py::init(), + py::arg("major"), py::arg("minor"), py::arg("subminor"), + py::arg("subsubminor"), + py::arg("release_type") = VersionInfo::RELEASE_FINAL) + .def(py::init(), py::arg("major"), + py::arg("minor"), py::arg("subminor"), + py::arg("release_type") = VersionInfo::RELEASE_FINAL) + .def("clear", &VersionInfo::clear) + .def("parse", &VersionInfo::parse, py::arg("value"), + py::arg("scheme") = VersionInfo::SCHEME_DISCOVER, + py::arg("is_manual") = false) + .def("canonicalString", &VersionInfo::canonicalString) + .def("displayString", &VersionInfo::displayString, + py::arg("forced_segments") = 2) + .def("isValid", &VersionInfo::isValid) + .def("scheme", &VersionInfo::scheme) + .def("__str__", &VersionInfo::canonicalString) + .def(py::self < py::self) + .def(py::self > py::self) + .def(py::self <= py::self) + .def(py::self >= py::self) + .def(py::self != py::self) + .def(py::self == py::self); + } + + void add_executable_classes(py::module_ m) + { + py::class_(m, "ExecutableInfo") + .def(py::init(), py::arg("title"), + py::arg("binary")) + .def("withArgument", &ExecutableInfo::withArgument, py::arg("argument")) + .def("withWorkingDirectory", &ExecutableInfo::withWorkingDirectory, + py::arg("directory")) + .def("withSteamAppId", &ExecutableInfo::withSteamAppId, py::arg("app_id")) + .def("asCustom", &ExecutableInfo::asCustom) + .def("isValid", &ExecutableInfo::isValid) + .def("title", &ExecutableInfo::title) + .def("binary", &ExecutableInfo::binary) + .def("arguments", &ExecutableInfo::arguments) + .def("workingDirectory", &ExecutableInfo::workingDirectory) + .def("steamAppID", &ExecutableInfo::steamAppID) + .def("isCustom", &ExecutableInfo::isCustom); + + py::class_(m, "ExecutableForcedLoadSetting") + .def(py::init(), py::arg("process"), + py::arg("library")) + .def("withForced", &ExecutableForcedLoadSetting::withForced, + py::arg("forced")) + .def("withEnabled", &ExecutableForcedLoadSetting::withEnabled, + py::arg("enabled")) + .def("enabled", &ExecutableForcedLoadSetting::enabled) + .def("forced", &ExecutableForcedLoadSetting::forced) + .def("library", &ExecutableForcedLoadSetting::library) + .def("process", &ExecutableForcedLoadSetting::process); + } + + void add_modinterface_classes(py::module_ m) + { + py::enum_(m, "EndorsedState") + .value("ENDORSED_FALSE", EndorsedState::ENDORSED_FALSE) + .value("ENDORSED_TRUE", EndorsedState::ENDORSED_TRUE) + .value("ENDORSED_UNKNOWN", EndorsedState::ENDORSED_UNKNOWN) + .value("ENDORSED_NEVER", EndorsedState::ENDORSED_NEVER); + + py::enum_(m, "TrackedState") + .value("TRACKED_FALSE", TrackedState::TRACKED_FALSE) + .value("TRACKED_TRUE", TrackedState::TRACKED_TRUE) + .value("TRACKED_UNKNOWN", TrackedState::TRACKED_UNKNOWN); + + // py::class_("IModInterface", + // py::no_init) + // .def("name", &IModInterface::name) + // .def("absolutePath", &IModInterface::absolutePath) + + // .def("comments", &IModInterface::comments) + // .def("notes", &IModInterface::notes) + // .def("gameName", &IModInterface::gameName) + // .def("repository", &IModInterface::repository) + // .def("nexusId", &IModInterface::nexusId) + // .def("version", &IModInterface::version) + // .def("newestVersion", &IModInterface::newestVersion) + // .def("ignoredVersion", &IModInterface::ignoredVersion) + // .def("installationFile", &IModInterface::installationFile) + // .def("converted", &IModInterface::converted) + // .def("validated", &IModInterface::validated) + // .def("color", &IModInterface::color) + // .def("url", &IModInterface::url) + // .def("primaryCategory", &IModInterface::primaryCategory) + // .def("categories", &IModInterface::categories) + // .def("trackedState", &IModInterface::trackedState) + // .def("endorsedState", &IModInterface::endorsedState) + // .def("fileTree", &IModInterface::fileTree) + // .def("isOverwrite", &IModInterface::isOverwrite) + // .def("isBackup", &IModInterface::isBackup) + // .def("isSeparator", &IModInterface::isSeparator) + // .def("isForeign", &IModInterface::isForeign) + + // .def("setVersion", &IModInterface::setVersion, + // py::arg("version")) .def("setNewestVersion", + // &IModInterface::setNewestVersion, py::arg("version")) + // .def("setIsEndorsed", &IModInterface::setIsEndorsed, + // py::arg("endorsed")) .def("setNexusID", + // &IModInterface::setNexusID, py::arg("nexus_id")) + // .def("addNexusCategory", &IModInterface::addNexusCategory, + // py::arg("category_id")) .def("addCategory", + // &IModInterface::addCategory, py::arg("name")) + // .def("removeCategory", &IModInterface::removeCategory, + // py::arg("name")) .def("setGameName", &IModInterface::setGameName, + // py::arg("name")) .def("setUrl", &IModInterface::setUrl, + // py::arg("url")) .def("pluginSetting", + // &IModInterface::pluginSetting, (py::arg("plugin_name"), "key", + // py::arg("default") = QVariant())) .def("pluginSettings", + // &IModInterface::pluginSettings, py::arg("plugin_name")) + // .def("setPluginSetting", &IModInterface::setPluginSetting, + // (py::arg("plugin_name"), "key", py::arg("value"))) + // .def("clearPluginSettings", &IModInterface::clearPluginSettings, + // py::arg("plugin_name")) + + // ; + } + + void add_modrepository_classes(py::module_ m) + { + // py::class_("IModRepositoryBridge", py::no_init) + // .def("requestDescription", + // &IModRepositoryBridge::requestDescription, (py::arg("game_name"), + // "mod_id", "user_data")) .def("requestFiles", + // &IModRepositoryBridge::requestFiles, (py::arg("game_name"), + // "mod_id", "user_data")) .def("requestFileInfo", + // &IModRepositoryBridge::requestFileInfo, (py::arg("game_name"), + // "mod_id", "file_id", "user_data")) .def("requestDownloadURL", + // &IModRepositoryBridge::requestDownloadURL, (py::arg("game_name"), + // "mod_id", "file_id", "user_data")) + // .def("requestToggleEndorsement", + // &IModRepositoryBridge::requestToggleEndorsement, + // (py::arg("game_name"), "mod_id", "mod_version", "endorse", + // "user_data")) + + // Q_DELEGATE(IModRepositoryBridge, QObject, "_object") + // ; + + // py::class_("ModRepositoryFileInfo", + // py::no_init) + // .def(py::init(py::arg("other"))) + // .def(py::init>((py::arg("game_name"), "mod_id", "file_id"))) + // .def("__str__", &ModRepositoryFileInfo::toString) + // .def("createFromJson", &ModRepositoryFileInfo::createFromJson, + // py::arg("data")).staticmethod("createFromJson") + // .def_readwrite("name", &ModRepositoryFileInfo::name) + // .def_readwrite("uri", &ModRepositoryFileInfo::uri) + // .def_readwrite("description", + // &ModRepositoryFileInfo::description) .def_readwrite("version", + // &ModRepositoryFileInfo::version) .def_readwrite("newestVersion", + // &ModRepositoryFileInfo::newestVersion) + // .def_readwrite("categoryID", &ModRepositoryFileInfo::categoryID) + // .def_readwrite("modName", &ModRepositoryFileInfo::modName) + // .def_readwrite("gameName", &ModRepositoryFileInfo::gameName) + // .def_readwrite("modID", &ModRepositoryFileInfo::modID) + // .def_readwrite("fileID", &ModRepositoryFileInfo::fileID) + // .def_readwrite("fileSize", &ModRepositoryFileInfo::fileSize) + // .def_readwrite("fileName", &ModRepositoryFileInfo::fileName) + // .def_readwrite("fileCategory", + // &ModRepositoryFileInfo::fileCategory) .def_readwrite("fileTime", + // &ModRepositoryFileInfo::fileTime) .def_readwrite("repository", + // &ModRepositoryFileInfo::repository) .def_readwrite("userData", + // &ModRepositoryFileInfo::userData) + // ; + } + + void add_guessedstring_classes(py::module_ m) + { + py::enum_(m, "GuessQuality") + .value("INVALID", MOBase::GUESS_INVALID) + .value("FALLBACK", MOBase::GUESS_FALLBACK) + .value("GOOD", MOBase::GUESS_GOOD) + .value("META", MOBase::GUESS_META) + .value("PRESET", MOBase::GUESS_PRESET) + .value("USER", MOBase::GUESS_USER); + + // py::class_, + // boost::noncopyable>("GuessedString") + // .def(py::init<>()) + // .def(py::init((py::arg("value"), + // py::arg("quality") = EGuessQuality::GUESS_USER))) .def("update", + // static_cast& + // (GuessedValue::*)(const + // QString&)>(&GuessedValue::update), + // py::return_self<>(), py::arg("value")) + // .def("update", + // static_cast& + // (GuessedValue::*)(const QString&, + // EGuessQuality)>(&GuessedValue::update), + // py::return_self<>(), (py::arg("value"), "quality")) + + // // Methods to simulate the assignment operator: + // .def("reset", +[](GuessedValue* gv) { + // *gv = GuessedValue(); }, py::return_self<>()) + // .def("reset", +[](GuessedValue* gv, const QString& + // value, EGuessQuality eq) { + // *gv = GuessedValue(value, eq); }, py::return_self<>(), + // (py::arg("value"), "quality")) + // .def("reset", +[](GuessedValue* gv, const + // GuessedValue& other) { + // *gv = other; }, py::return_self<>(), py::arg("other")) + + // // Use an intermediate lambda to avoid having to register the + // std::function conversion: .def("setFilter", + // +[](GuessedValue* gv, + // std::function(QString const&)> fn) { + // gv->setFilter([fn](QString& s) { + // auto ret = fn(s); + // return std::visit([&s](auto v) { + // if constexpr (std::is_same_v) { + // s = v; + // return true; + // } + // else if constexpr (std::is_same_v) { + // return v; + // } + // }, ret); + // }); + // }, py::arg("filter")) + + // // This makes a copy in python but it more practical than + // exposing an iterator: .def("variants", + // &GuessedValue::variants, + // py::return_value_policy()) + // .def("__str__", &MOBase::GuessedValue::operator const + // QString&, py::return_value_policy()) + // ; + } + + void add_ipluginlist_classes(py::module_ m) + { + py::enum_(m, "PluginState") + .value("missing", IPluginList::STATE_MISSING) + .value("inactive", IPluginList::STATE_INACTIVE) + .value("active", IPluginList::STATE_ACTIVE) + + .value("MISSING", IPluginList::STATE_MISSING) + .value("INACTIVE", IPluginList::STATE_INACTIVE) + .value("ACTIVE", IPluginList::STATE_ACTIVE); + + // py::class_("IPluginList", + // py::no_init) + // .def("state", &MOBase::IPluginList::state, py::arg("name")) + // .def("priority", &MOBase::IPluginList::priority, py::arg("name")) + // .def("setPriority", &MOBase::IPluginList::setPriority, + // (py::arg("name"), "priority")) .def("loadOrder", + // &MOBase::IPluginList::loadOrder, py::arg("name")) + // .def("isMaster", &MOBase::IPluginList::isMaster, py::arg("name")) + // .def("masters", &MOBase::IPluginList::masters, py::arg("name")) + // .def("origin", &MOBase::IPluginList::origin, py::arg("name")) + // .def("onRefreshed", &MOBase::IPluginList::onRefreshed, + // py::arg("callback")) .def("onPluginMoved", + // &MOBase::IPluginList::onPluginMoved, py::arg("callback")) + + // // Kept but deprecated for backward compatibility: + // .def("onPluginStateChanged", +[](IPluginList* modList, const + // std::function& + // fn) { + // utils::show_deprecation_warning("onPluginStateChanged", + // "onPluginStateChanged(Callable[[str, + // IPluginList.PluginStates], None]) is deprecated, " "use + // onPluginStateChanged(Callable[[Dict[str, + // IPluginList.PluginStates], None]) instead."); + // return modList->onPluginStateChanged([fn](auto const& map) { + // for (const auto& entry : map) { + // fn(entry.first, entry.second); + // } + // }); + // }, py::arg("callback")) + // .def("onPluginStateChanged", + // &MOBase::IPluginList::onPluginStateChanged, py::arg("callback")) + // .def("pluginNames", &MOBase::IPluginList::pluginNames) + // .def("setState", &MOBase::IPluginList::setState, + // (py::arg("name"), "state")) .def("setLoadOrder", + // &MOBase::IPluginList::setLoadOrder, py::arg("loadorder")) + // ; + } + + void add_imodlist_classes(py::module_ m) + { + // py::enum_("ModState") + // .value("exists", IModList::STATE_EXISTS) + // .value("active", IModList::STATE_ACTIVE) + // .value("essential", IModList::STATE_ESSENTIAL) + // .value("empty", IModList::STATE_EMPTY) + // .value("endorsed", IModList::STATE_ENDORSED) + // .value("valid", IModList::STATE_VALID) + // .value("alternate", IModList::STATE_ALTERNATE) + + // .value("EXISTS", IModList::STATE_EXISTS) + // .value("ACTIVE", IModList::STATE_ACTIVE) + // .value("ESSENTIAL", IModList::STATE_ESSENTIAL) + // .value("EMPTY", IModList::STATE_EMPTY) + // .value("ENDORSED", IModList::STATE_ENDORSED) + // .value("VALID", IModList::STATE_VALID) + // .value("ALTERNATE", IModList::STATE_ALTERNATE) + // ; + + // py::class_("IModList", py::no_init) + // .def("displayName", &MOBase::IModList::displayName, + // py::arg("name")) .def("allMods", &MOBase::IModList::allMods) + // .def("allModsByProfilePriority", + // &MOBase::IModList::allModsByProfilePriority, py::arg("profile") = + // py::ptr((IProfile*)nullptr)) + + // .def("getMod", &MOBase::IModList::getMod, + // py::return_value_policy(), + // py::arg("name")) .def("removeMod", &MOBase::IModList::removeMod, + // py::arg("mod")) .def("renameMod", &MOBase::IModList::renameMod, + // py::return_value_policy(), + // (py::arg("mod"), py::arg("name"))) + + // .def("state", &MOBase::IModList::state, py::arg("name")) + // .def("setActive", + // static_cast(&MOBase::IModList::setActive), (py::arg("names"), + // "active")) + // .def("setActive", + // static_cast(&MOBase::IModList::setActive), (py::arg("name"), + // "active")) + // .def("priority", &MOBase::IModList::priority, py::arg("name")) + // .def("setPriority", &MOBase::IModList::setPriority, + // (py::arg("name"), "priority")) + + // // Kept but deprecated for backward compatibility: + // .def("onModStateChanged", +[](IModList* modList, const + // std::function& fn) { + // utils::show_deprecation_warning("onModStateChanged", + // "onModStateChanged(Callable[[str, IModList.ModStates], None]) + // is deprecated, " "use onModStateChanged(Callable[[Dict[str, + // IModList.ModStates], None]) instead."); + // return modList->onModStateChanged([fn](auto const& map) { + // for (const auto& entry : map) { + // fn(entry.first, entry.second); + // } + // }); + // }, py::arg("callback")) + + // .def("onModInstalled", &MOBase::IModList::onModInstalled, + // py::arg("callback")) .def("onModRemoved", + // &MOBase::IModList::onModRemoved, py::arg("callback")) + // .def("onModStateChanged", &MOBase::IModList::onModStateChanged, + // py::arg("callback")) .def("onModMoved", + // &MOBase::IModList::onModMoved, py::arg("callback")) + // ; + } + + void add_iorganizer_classes(py::module_ m) + { + // py::class_("FileInfo", py::init<>()) + // .add_property("filePath", + // +[](const IOrganizer::FileInfo& info) { return info.filePath; }, + // +[](IOrganizer::FileInfo& info, QString value) { info.filePath = + // value; + // }) + // .add_property("archive", + // +[](const IOrganizer::FileInfo& info) { return info.archive; }, + // +[](IOrganizer::FileInfo& info, QString value) { info.archive = + // value; + // }) + // .add_property("origins", + // +[](const IOrganizer::FileInfo& info) { return info.origins; }, + // +[](IOrganizer::FileInfo& info, QStringList value) { info.origins + // = value; }) + // ; + + // py::class_("IOrganizer", py::no_init) + // .def("createNexusBridge", &IOrganizer::createNexusBridge, + // py::return_value_policy()) + // .def("profileName", &IOrganizer::profileName) + // .def("profilePath", &IOrganizer::profilePath) + // .def("downloadsPath", &IOrganizer::downloadsPath) + // .def("overwritePath", &IOrganizer::overwritePath) + // .def("basePath", &IOrganizer::basePath) + // .def("modsPath", &IOrganizer::modsPath) + // .def("appVersion", &IOrganizer::appVersion) + // .def("createMod", &IOrganizer::createMod, + // py::return_value_policy(), + // py::arg("name")) .def("getGame", &IOrganizer::getGame, + // py::return_value_policy(), + // py::arg("name")) .def("modDataChanged", + // &IOrganizer::modDataChanged, py::arg("mod")) + // .def("isPluginEnabled", +[](IOrganizer* o, IPlugin* plugin) { + // return o->isPluginEnabled(plugin); }, py::arg("plugin")) + // .def("isPluginEnabled", +[](IOrganizer* o, QString const& plugin) + // { return o->isPluginEnabled(plugin); }, py::arg("plugin")) + // .def("pluginSetting", &IOrganizer::pluginSetting, + // (py::arg("plugin_name"), "key")) .def("setPluginSetting", + // &IOrganizer::setPluginSetting, (py::arg("plugin_name"), "key", + // "value")) .def("persistent", &IOrganizer::persistent, + // (py::arg("plugin_name"), "key", py::arg("default") = QVariant())) + // .def("setPersistent", &IOrganizer::setPersistent, + // (py::arg("plugin_name"), "key", "value", py::arg("sync") = true)) + // .def("pluginDataPath", &IOrganizer::pluginDataPath) + // .def("installMod", &IOrganizer::installMod, + // py::return_value_policy(), + // (py::arg("filename"), py::arg("name_suggestion") = "")) + // .def("resolvePath", &IOrganizer::resolvePath, + // py::arg("filename")) .def("listDirectories", + // &IOrganizer::listDirectories, py::arg("directory")) + + // // Provide multiple overloads of findFiles: + // .def("findFiles", +[](const IOrganizer* o, QString const& p, + // std::function f) { return o->findFiles(p, + // f); + // }, + // (py::arg("path"), "filter")) + + // // In C++, it is possible to create a QStringList implicitly from + // a single QString. This is not possible with the current + // // converters in python (and I do not think it is a good idea to + // have it everywhere), but here it is nice to be able to + // // pass a single string, so we add an extra overload. + // // Important: the order matters, because a Python string can be + // converted to a QStringList since it is a sequence of + // // single-character strings: + // .def("findFiles", +[](const IOrganizer* o, QString const& p, + // const QStringList& gf) { return o->findFiles(p, gf); }, + // (py::arg("path"), "patterns")) + // .def("findFiles", +[](const IOrganizer* o, QString const& p, + // const QString& f) { return o->findFiles(p, QStringList{ f }); }, + // (py::arg("path"), "pattern")) + + // .def("getFileOrigins", &IOrganizer::getFileOrigins, + // py::arg("filename")) .def("findFileInfos", + // &IOrganizer::findFileInfos, (py::arg("path"), "filter")) + + // .def("virtualFileTree", &IOrganizer::virtualFileTree) + + // .def("downloadManager", &IOrganizer::downloadManager, + // py::return_value_policy()) + // .def("pluginList", &IOrganizer::pluginList, + // py::return_value_policy()) + // .def("modList", &IOrganizer::modList, + // py::return_value_policy()) + // .def("profile", &IOrganizer::profile, + // py::return_value_policy()) + + // // Custom implementation for startApplication and + // waitForApplication because 1) HANDLE (= void*) is not properly + // // converted from/to python, and 2) we need to convert the by-ptr + // argument to a return-tuple for waitForApplication: + // .def("startApplication", + // +[](IOrganizer* o, const QString& executable, const + // QStringList& args, const QString& cwd, const QString& profile, + // const QString& forcedCustomOverwrite, bool + // ignoreCustomOverwrite) + // { + // return (std::uintptr_t) o->startApplication(executable, + // args, cwd, profile, forcedCustomOverwrite, + // ignoreCustomOverwrite); + // }, (py::arg("executable"), (py::arg("args") = QStringList()), + // (py::arg("cwd") = ""), (py::arg("profile") = ""), + // (py::arg("forcedCustomOverwrite") = ""), + // (py::arg("ignoreCustomOverwrite") = false)), + // py::return_value_policy()) + // .def("waitForApplication", +[](IOrganizer *o, std::uintptr_t + // handle, bool refresh) { + // DWORD returnCode; + // bool result = o->waitForApplication((HANDLE)handle, refresh, + // &returnCode); return std::make_tuple(result, returnCode); + // }, (py::arg("handle"), py::arg("refresh") = true)) + // .def("refresh", &IOrganizer::refresh, (py::arg("save_changes") = + // true)) .def("managedGame", &IOrganizer::managedGame, + // py::return_value_policy()) + + // .def("onAboutToRun", &IOrganizer::onAboutToRun, + // py::arg("callback")) .def("onFinishedRun", + // &IOrganizer::onFinishedRun, py::arg("callback")) + // .def("onUserInterfaceInitialized", + // &IOrganizer::onUserInterfaceInitialized, py::arg("callback")) + // .def("onProfileCreated", &IOrganizer::onProfileCreated, + // py::arg("callback")) .def("onProfileRenamed", + // &IOrganizer::onProfileRenamed, py::arg("callback")) + // .def("onProfileRemoved", &IOrganizer::onProfileRemoved, + // py::arg("callback")) .def("onProfileChanged", + // &IOrganizer::onProfileChanged, py::arg("callback")) + + // .def("onPluginSettingChanged", + // &IOrganizer::onPluginSettingChanged, py::arg("callback")) + // .def("onPluginEnabled", +[](IOrganizer* o, + // std::function const& func) { + // o->onPluginEnabled(func); + // }, py::arg("callback")) + // .def("onPluginEnabled", +[](IOrganizer* o, QString const& name, + // std::function const& func) { + // o->onPluginEnabled(name, func); + // }, (py::arg("name"), py::arg("callback"))) + // .def("onPluginDisabled", +[](IOrganizer* o, + // std::function const& func) { + // o->onPluginDisabled(func); + // }, py::arg("callback")) + // .def("onPluginDisabled", +[](IOrganizer* o, QString const& name, + // std::function const& func) { + // o->onPluginDisabled(name, func); + // }, (py::arg("name"), py::arg("callback"))) + + // // DEPRECATED: + // .def("getMod", +[](IOrganizer* o, QString const& name) { + // utils::show_deprecation_warning("getMod", + // "IOrganizer::getMod(str) is deprecated, use + // IModList::getMod(str) instead."); + // return o->modList()->getMod(name); + // }, py::return_value_policy(), + // py::arg("name")) .def("removeMod", +[](IOrganizer* o, + // IModInterface *mod) { + // utils::show_deprecation_warning("removeMod", + // "IOrganizer::removeMod(IModInterface) is deprecated, use + // IModList::removeMod(IModInterface) instead."); + // return o->modList()->removeMod(mod); + // }, py::arg("mod")) + // .def("modsSortedByProfilePriority", +[](IOrganizer* o) { + // utils::show_deprecation_warning("modsSortedByProfilePriority", + // "IOrganizer::modsSortedByProfilePriority() is deprecated, + // use IModList::allModsByProfilePriority() instead."); + // return o->modList()->allModsByProfilePriority(); + // }) + // .def("refreshModList", +[](IOrganizer* o, bool s) { + // utils::show_deprecation_warning("refreshModList", + // "IOrganizer::refreshModList(bool) is deprecated, use + // IOrganizer::refresh(bool) instead."); + // o->refresh(s); + // }, (py::arg("save_changes") = true)) + // .def("onModInstalled", +[](IOrganizer* organizer, const + // std::function& func) { + // utils::show_deprecation_warning("onModInstalled", + // "IOrganizer::onModInstalled(Callable[[str], None]) is + // deprecated, " "use + // IModList::onModInstalled(Callable[[IModInterface], None]) + // instead."); + // return + // organizer->modList()->onModInstalled([func](MOBase::IModInterface* + // m) { func(m->name()); });; + // }, py::arg("callback")) + + // .def("getPluginDataPath", &IOrganizer::getPluginDataPath) + // .staticmethod("getPluginDataPath") + + // ; + } + + void add_idownload_manager_classes(py::module_ m) + { + // py::class_("IDownloadManager", + // py::no_init) + // .def("startDownloadURLs", &IDownloadManager::startDownloadURLs, + // py::arg("urls")) .def("startDownloadNexusFile", + // &IDownloadManager::startDownloadNexusFile, (py::arg("mod_id"), + // "file_id")) .def("downloadPath", &IDownloadManager::downloadPath, + // py::arg("id")) .def("onDownloadComplete", + // &IDownloadManager::onDownloadComplete, py::arg("callback")) + // .def("onDownloadPaused", &IDownloadManager::onDownloadPaused, + // py::arg("callback")) .def("onDownloadFailed", + // &IDownloadManager::onDownloadFailed, py::arg("callback")) + // .def("onDownloadRemoved", &IDownloadManager::onDownloadRemoved, + // py::arg("callback")) + // ; + } + + void add_iinstallation_manager_classes(py::module_ m) + { + // py::class_("IInstallationManager", py::no_init) + // .def("getSupportedExtensions", + // &IInstallationManager::getSupportedExtensions) .def("extractFile", + // &IInstallationManager::extractFile, (py::arg("entry"), + // py::arg("silent") = false)) .def("extractFiles", + // &IInstallationManager::extractFiles, (py::arg("entries"), + // py::arg("silent") = false)) .def("createFile", + // +[](IInstallationManager* m, std::shared_ptr entry) { + // return m->createFile(utils::clean_shared_ptr(entry)); + // }, py::arg("entry")) + + // // accept both QString and GuessedValue since the + // conversion is not automatic in Python, and + // // return a tuple to get back the mod name and the mod ID + // .def("installArchive", +[](IInstallationManager* m, + // std::variant> modName, QString + // archive, int modId) { + // GuessedValue tmp; + // if (auto* p = std::get_if(&modName)) { + // tmp = *p; + // } + // else { + // tmp = std::get>(modName); + // } + // auto result = m->installArchive(tmp, archive, modId); + // return std::make_tuple(result, static_cast(tmp), modId); + // }, (py::arg("mod_name"), "archive", py::arg("mod_id") = 0)) + // ; + } + + void add_basic_bindings(py::module_ m) + { + add_versioninfo_classes(m); + add_executable_classes(m); + + add_ifiletree_bindings(m); + + add_modinterface_classes(m); + add_modrepository_classes(m); + + add_ipluginlist_classes(m); + add_imodlist_classes(m); + add_iorganizer_classes(m); + add_idownload_manager_classes(m); + add_iinstallation_manager_classes(m); + + py::class_(m, "PluginSetting") + .def(py::init(), + py::arg("key"), py::arg("description"), py::arg("default_value")) + .def_readwrite("key", &PluginSetting::key) + .def_readwrite("description", &PluginSetting::description) + .def_readwrite("default_value", &PluginSetting::defaultValue); + + py::class_(m, "IProfile") + .def("name", &IProfile::name) + .def("absolutePath", &IProfile::absolutePath) + .def("localSavesEnabled", &IProfile::localSavesEnabled) + .def("localSettingsEnabled", &IProfile::localSettingsEnabled) + .def("invalidationActive", + [](const IProfile* p) { + bool supported; + bool active = p->invalidationActive(&supported); + return py::make_tuple(active, supported); + }) + .def("absoluteIniFilePath", &IProfile::absoluteIniFilePath, + py::arg("inifile")); + + // py::class_("PluginRequirementFactory") + // // pluginDependency + // .def("pluginDependency", +[](QStringList const& pluginNames) { + // return PluginRequirementFactory::pluginDependency(pluginNames); + // }, py::arg("plugins")) + // .def("pluginDependency", +[](QString const& pluginName) { + // return PluginRequirementFactory::pluginDependency(pluginName); + // }, py::arg("plugin")) + // .staticmethod("pluginDependency") + // // gameDependency + // .def("gameDependency", +[](QStringList const& gameNames) { + // return PluginRequirementFactory::gameDependency(gameNames); + // }, py::arg("games")) + // .def("gameDependency", +[](QString const& gameNames) { + // return PluginRequirementFactory::gameDependency(gameNames); + // }, py::arg("game")) + // .staticmethod("gameDependency") + // // diagnose + // .def("diagnose", &PluginRequirementFactory::diagnose, + // py::arg("diagnose")) .staticmethod("diagnose") + // // basic + // .def("basic", &PluginRequirementFactory::basic, + // (py::arg("checker"), "description")) .staticmethod("basic"); + // py::class_("Mapping", py::init<>()) + // .def("__init__", py::make_constructor(+[](QString src, QString + // dst, bool dir, bool crt) -> Mapping* { + // return new Mapping{ src, dst, dir, crt }; + // }, py::default_call_policies(), + // (py::arg("source"), py::arg("destination"), + // py::arg("is_directory"), py::arg("create_target") = false))) + // .def_readwrite("source", &Mapping::source) + // .def_readwrite("destination", &Mapping::destination) + // .def_readwrite("isDirectory", &Mapping::isDirectory) + // .def_readwrite("createTarget", &Mapping::createTarget) + // .def("__str__", +[](Mapping * m) { + // return fmt::format(L"Mapping({}, {}, {}, {})", + // m->source.toStdWString(), m->destination.toStdWString(), + // m->isDirectory, m->createTarget); + // }) + // ; + + // py::class_("Mapping", py::init<>()) + // .def("__init__", py::make_constructor(+[](QString src, QString + // dst, bool dir, bool crt) -> Mapping* { + // return new Mapping{ src, dst, dir, crt }; + // }, py::default_call_policies(), + // (py::arg("source"), py::arg("destination"), + // py::arg("is_directory"), py::arg("create_target") = false))) + // .def_readwrite("source", &Mapping::source) + // .def_readwrite("destination", &Mapping::destination) + // .def_readwrite("isDirectory", &Mapping::isDirectory) + // .def_readwrite("createTarget", &Mapping::createTarget) + // .def("__str__", +[](Mapping * m) { + // return fmt::format(L"Mapping({}, {}, {}, {})", + // m->source.toStdWString(), m->destination.toStdWString(), + // m->isDirectory, m->createTarget); + // }) + // ; + } + +} // namespace mo2::python diff --git a/src/runner-pybind11/wrappers/game_features.cpp b/src/runner-pybind11/wrappers/game_features.cpp new file mode 100644 index 0000000..a39cacd --- /dev/null +++ b/src/runner-pybind11/wrappers/game_features.cpp @@ -0,0 +1,19 @@ +#include "wrappers.h" + +#include +#include +#include +#include + +#include "../pybind11_qt/pybind11_qt.h" + +#include + +namespace py = pybind11; +using namespace MOBase; + +namespace mo2::python { + + void add_game_feature_bindings(pybind11::module_ m) {} + +} // namespace mo2::python diff --git a/src/runner-pybind11/wrappers/plugins.cpp b/src/runner-pybind11/wrappers/plugins.cpp new file mode 100644 index 0000000..b2c8005 --- /dev/null +++ b/src/runner-pybind11/wrappers/plugins.cpp @@ -0,0 +1,390 @@ +#include "wrappers.h" + +#include +#include +#include +#include + +#include "../pybind11_qt/pybind11_qt.h" + +#include + +namespace py = pybind11; +using namespace MOBase; + +namespace mo2::python { + + void add_plugins_bindings(pybind11::module_ m) + { + // // Note: localizedName, master, requirements and enabledByDefault + // have to + // go in all the plugin wrappers declaration, + // // since the default functions are specific to each wrapper, + // otherwise in turns into an + // // infinite recursion mess. + // py::class_("IPlugin") + // .def("init", py::pure_virtual(&MOBase::IPlugin::init), + // py::arg("organizer")) .def("name", + // py::pure_virtual(&MOBase::IPlugin::name)) .def("localizedName", + // &MOBase::IPlugin::localizedName, + // &IPluginWrapper::localizedName_Default) .def("master", + // &MOBase::IPlugin::master, &IPluginWrapper::master_Default) + // .def("author", py::pure_virtual(&MOBase::IPlugin::author)) + // .def("description", + // py::pure_virtual(&MOBase::IPlugin::description)) .def("version", + // py::pure_virtual(&MOBase::IPlugin::version)) .def("requirements", + // &MOBase::IPlugin::requirements, + // &IPluginWrapper::requirements_Default) .def("settings", + // py::pure_virtual(&MOBase::IPlugin::settings)) + // .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, + // &IPluginWrapper::enabledByDefault_Default) + // ; + + // py::class_, + // boost::noncopyable>("IPluginDiagnose") + // .def("localizedName", &MOBase::IPlugin::localizedName, + // &IPluginDiagnoseWrapper::localizedName_Default) .def("master", + // &MOBase::IPlugin::master, + // &IPluginDiagnoseWrapper::master_Default) .def("requirements", + // &MOBase::IPlugin::requirements, + // &IPluginDiagnoseWrapper::requirements_Default) + // .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, + // &IPluginDiagnoseWrapper::enabledByDefault_Default) + + // .def("activeProblems", + // py::pure_virtual(&MOBase::IPluginDiagnose::activeProblems)) + // .def("shortDescription", + // py::pure_virtual(&MOBase::IPluginDiagnose::shortDescription), + // py::arg("key")) .def("fullDescription", + // py::pure_virtual(&MOBase::IPluginDiagnose::fullDescription), + // py::arg("key")) .def("hasGuidedFix", + // py::pure_virtual(&MOBase::IPluginDiagnose::hasGuidedFix), + // py::arg("key")) .def("startGuidedFix", + // py::pure_virtual(&MOBase::IPluginDiagnose::startGuidedFix), + // py::arg("key")) .def("_invalidate", + // &IPluginDiagnoseWrapper::invalidate) + // ; + // py::class_, + // boost::noncopyable>("IPluginFileMapper") + // .def("localizedName", &MOBase::IPlugin::localizedName, + // &IPluginFileMapperWrapper::localizedName_Default) .def("master", + // &MOBase::IPlugin::master, + // &IPluginFileMapperWrapper::master_Default) .def("requirements", + // &MOBase::IPlugin::requirements, + // &IPluginFileMapperWrapper::requirements_Default) + // .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, + // &IPluginFileMapperWrapper::enabledByDefault_Default) + + // .def("mappings", + // py::pure_virtual(&MOBase::IPluginFileMapper::mappings)) + // ; + + // py::enum_("LoadOrderMechanism") + // .value("FileTime", + // MOBase::IPluginGame::LoadOrderMechanism::FileTime) + // .value("PluginsTxt", + // MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt) + + // .value("FILE_TIME", + // MOBase::IPluginGame::LoadOrderMechanism::FileTime) + // .value("PLUGINS_TXT", + // MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt) + // ; + + // py::enum_("SortMechanism") + // .value("NONE", MOBase::IPluginGame::SortMechanism::NONE) + // .value("MLOX", MOBase::IPluginGame::SortMechanism::MLOX) + // .value("BOSS", MOBase::IPluginGame::SortMechanism::BOSS) + // .value("LOOT", MOBase::IPluginGame::SortMechanism::LOOT) + // ; + + // // This doesn't actually do the conversion, but might be convenient + // for accessing the names for enum bits + // py::enum_("ProfileSetting") + // .value("mods", MOBase::IPluginGame::MODS) + // .value("configuration", MOBase::IPluginGame::CONFIGURATION) + // .value("savegames", MOBase::IPluginGame::SAVEGAMES) + // .value("preferDefaults", MOBase::IPluginGame::PREFER_DEFAULTS) + + // .value("MODS", MOBase::IPluginGame::MODS) + // .value("CONFIGURATION", MOBase::IPluginGame::CONFIGURATION) + // .value("SAVEGAMES", MOBase::IPluginGame::SAVEGAMES) + // .value("PREFER_DEFAULTS", MOBase::IPluginGame::PREFER_DEFAULTS) + // ; + + // py::class_, + // boost::noncopyable>("IPluginGame") + // .def("localizedName", &MOBase::IPlugin::localizedName, + // &IPluginGameWrapper::localizedName_Default) .def("master", + // &MOBase::IPlugin::master, &IPluginGameWrapper::master_Default) + + // .def("detectGame", + // py::pure_virtual(&MOBase::IPluginGame::detectGame)) + // .def("gameName", + // py::pure_virtual(&MOBase::IPluginGame::gameName)) + // .def("initializeProfile", + // py::pure_virtual(&MOBase::IPluginGame::initializeProfile), + // (py::arg("directory"), "settings")) .def("listSaves", + // py::pure_virtual(&MOBase::IPluginGame::listSaves), + // py::arg("folder")) .def("isInstalled", + // py::pure_virtual(&MOBase::IPluginGame::isInstalled)) + // .def("gameIcon", + // py::pure_virtual(&MOBase::IPluginGame::gameIcon)) + // .def("gameDirectory", + // py::pure_virtual(&MOBase::IPluginGame::gameDirectory)) + // .def("dataDirectory", + // py::pure_virtual(&MOBase::IPluginGame::dataDirectory)) + // .def("setGamePath", + // py::pure_virtual(&MOBase::IPluginGame::setGamePath), + // py::arg("path")) .def("documentsDirectory", + // py::pure_virtual(&MOBase::IPluginGame::documentsDirectory)) + // .def("savesDirectory", + // py::pure_virtual(&MOBase::IPluginGame::savesDirectory)) + // .def("executables", + // py::pure_virtual(&MOBase::IPluginGame::executables)) + // .def("executableForcedLoads", + // py::pure_virtual(&MOBase::IPluginGame::executableForcedLoads)) + // .def("steamAPPId", + // py::pure_virtual(&MOBase::IPluginGame::steamAPPId)) + // .def("primaryPlugins", + // py::pure_virtual(&MOBase::IPluginGame::primaryPlugins)) + // .def("gameVariants", + // py::pure_virtual(&MOBase::IPluginGame::gameVariants)) + // .def("setGameVariant", + // py::pure_virtual(&MOBase::IPluginGame::setGameVariant), + // py::arg("variant")) .def("binaryName", + // py::pure_virtual(&MOBase::IPluginGame::binaryName)) + // .def("gameShortName", + // py::pure_virtual(&MOBase::IPluginGame::gameShortName)) + // .def("primarySources", + // py::pure_virtual(&MOBase::IPluginGame::primarySources)) + // .def("validShortNames", + // py::pure_virtual(&MOBase::IPluginGame::validShortNames)) + // .def("gameNexusName", + // py::pure_virtual(&MOBase::IPluginGame::gameNexusName)) + // .def("iniFiles", + // py::pure_virtual(&MOBase::IPluginGame::iniFiles)) + // .def("DLCPlugins", + // py::pure_virtual(&MOBase::IPluginGame::DLCPlugins)) + // .def("CCPlugins", + // py::pure_virtual(&MOBase::IPluginGame::CCPlugins)) + // .def("loadOrderMechanism", + // py::pure_virtual(&MOBase::IPluginGame::loadOrderMechanism)) + // .def("sortMechanism", + // py::pure_virtual(&MOBase::IPluginGame::sortMechanism)) + // .def("nexusModOrganizerID", + // py::pure_virtual(&MOBase::IPluginGame::nexusModOrganizerID)) + // .def("nexusGameID", + // py::pure_virtual(&MOBase::IPluginGame::nexusGameID)) + // .def("looksValid", + // py::pure_virtual(&MOBase::IPluginGame::looksValid), + // py::arg("directory")) .def("gameVersion", + // py::pure_virtual(&MOBase::IPluginGame::gameVersion)) + // .def("getLauncherName", + // py::pure_virtual(&MOBase::IPluginGame::getLauncherName)) + + // .def("featureList", +[](MOBase::IPluginGame* p) { + // // Constructing a dict from class name to actual object: + // py::dict dict; + // mp11::mp_for_each< + // // Must user pointers because mp_for_each construct object: + // mp11::mp_transform + // >([&](auto* pt) { + // using T = std::remove_pointer_t; + // typename py::reference_existing_object::apply::type + // converter; + + // // Retrieve the python class object: + // const py::converter::registration* registration = + // py::converter::registry::query(py::type_id()); py::object + // key + // = + // py::object(py::handle<>(py::borrowed(registration->get_class_object()))); + + // // Set the object: + // dict[key] = py::handle<>(converter(p->feature())); + // }); + // return dict; + // }) + + // .def("feature", +[](MOBase::IPluginGame* p, py::object clsObj) { + // py::object feature; + // mp11::mp_for_each< + // // Must user pointers because mp_for_each construct object: + // mp11::mp_transform + // >([&](auto* pt) { + // using T = std::remove_pointer_t; + // typename py::reference_existing_object::apply::type + // converter; + + // // Retrieve the python class object: + // const py::converter::registration* registration = + // py::converter::registry::query(py::type_id()); + + // if (clsObj.ptr() == (PyObject*) + // registration->get_class_object()) + // { + // feature = + // py::object(py::handle<>(converter(p->feature()))); + // } + // }); + // return feature; + // }, py::arg("feature_type")) + // ; + + // py::enum_("InstallResult") + // .value("SUCCESS", MOBase::IPluginInstaller::RESULT_SUCCESS) + // .value("FAILED", MOBase::IPluginInstaller::RESULT_FAILED) + // .value("CANCELED", MOBase::IPluginInstaller::RESULT_CANCELED) + // .value("MANUAL_REQUESTED", + // MOBase::IPluginInstaller::RESULT_MANUALREQUESTED) + // .value("NOT_ATTEMPTED", + // MOBase::IPluginInstaller::RESULT_NOTATTEMPTED) + // ; + + // py::class_, + // boost::noncopyable>("IPluginInstaller", py::no_init) + // .def("isArchiveSupported", &IPluginInstaller::isArchiveSupported, + // py::arg("tree")) .def("priority", &IPluginInstaller::priority) + // .def("onInstallationStart", &IPluginInstaller::onInstallationStart, + // (py::arg("archive"), py::arg("reinstallation"), + // py::arg("current_mod"))) .def("onInstallationEnd", + // &IPluginInstaller::onInstallationEnd, (py::arg("result"), + // py::arg("new_mod"))) .def("isManualInstaller", + // &IPluginInstaller::isManualInstaller) .def("setParentWidget", + // &IPluginInstaller::setParentWidget, py::arg("parent")) + // .def("setInstallationManager", + // &IPluginInstaller::setInstallationManager, py::arg("manager")) + // ; + + // py::class_, + // boost::noncopyable>("IPluginInstallerSimple") + // .def("onInstallationStart", &IPluginInstaller::onInstallationStart, + // (py::arg("archive"), py::arg("reinstallation"), + // py::arg("current_mod"))) .def("onInstallationEnd", + // &IPluginInstaller::onInstallationEnd, (py::arg("result"), + // py::arg("new_mod"))) .def("localizedName", + // &MOBase::IPlugin::localizedName, + // &IPluginInstallerSimpleWrapper::localizedName_Default) + // .def("master", &MOBase::IPlugin::master, + // &IPluginInstallerSimpleWrapper::master_Default) + // .def("requirements", &MOBase::IPlugin::requirements, + // &IPluginInstallerSimpleWrapper::requirements_Default) + // .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, + // &IPluginInstallerSimpleWrapper::enabledByDefault_Default) + + // // Note: Keeping the variant here even if we always return a tuple + // to be consistent with the wrapper and + // // have proper stubs generation. + // .def("install", +[](IPluginInstallerSimple* p, + // GuessedValue& modName, std::shared_ptr& tree, + // QString& version, int& nexusID) + // -> std::variant, + // std::tuple, QString, int>> { + // auto result = p->install(modName, tree, version, nexusID); + // return std::make_tuple(result, tree, version, nexusID); + // }, (py::arg("name"), "tree", "version", "nexus_id")) + // .def("_parentWidget", &IPluginInstallerSimpleWrapper::parentWidget, + // py::return_value_policy()) .def("_manager", + // &IPluginInstallerSimpleWrapper::manager, + // py::return_value_policy()) + // ; + + // py::class_, + // boost::noncopyable>("IPluginInstallerCustom") + // .def("onInstallationStart", &IPluginInstaller::onInstallationStart, + // (py::arg("archive"), py::arg("reinstallation"), + // py::arg("current_mod"))) .def("onInstallationEnd", + // &IPluginInstaller::onInstallationEnd, (py::arg("result"), + // py::arg("new_mod"))) .def("localizedName", + // &MOBase::IPlugin::localizedName, + // &IPluginInstallerCustomWrapper::localizedName_Default) + // .def("master", &MOBase::IPlugin::master, + // &IPluginInstallerCustomWrapper::master_Default) + // .def("requirements", &MOBase::IPlugin::requirements, + // &IPluginInstallerCustomWrapper::requirements_Default) + // .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, + // &IPluginInstallerCustomWrapper::enabledByDefault_Default) + + // // Needs to add both otherwize boost does not understand: + // .def("isArchiveSupported", &IPluginInstaller::isArchiveSupported, + // py::arg("tree")) .def("isArchiveSupported", + // &IPluginInstallerCustom::isArchiveSupported, + // py::arg("archive_name")) .def("supportedExtensions", + // &IPluginInstallerCustom::supportedExtensions) .def("install", + // &IPluginInstallerCustom::install, (py::arg("mod_name"), + // "game_name", "archive_name", "version", "nexus_id")) + // .def("_parentWidget", &IPluginInstallerSimpleWrapper::parentWidget, + // py::return_value_policy()) .def("_manager", + // &IPluginInstallerCustomWrapper::manager, + // py::return_value_policy()) + // ; + + // py::class_, + // boost::noncopyable>("IPluginModPage") + // .def("localizedName", &MOBase::IPlugin::localizedName, + // &IPluginModPageWrapper::localizedName_Default) .def("master", + // &MOBase::IPlugin::master, &IPluginModPageWrapper::master_Default) + // .def("requirements", &MOBase::IPlugin::requirements, + // &IPluginModPageWrapper::requirements_Default) + // .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, + // &IPluginModPageWrapper::enabledByDefault_Default) + + // .def("displayName", py::pure_virtual(&IPluginModPage::displayName)) + // .def("icon", py::pure_virtual(&IPluginModPage::icon)) + // .def("pageURL", py::pure_virtual(&IPluginModPage::pageURL)) + // .def("useIntegratedBrowser", + // py::pure_virtual(&IPluginModPage::useIntegratedBrowser)) + // .def("handlesDownload", + // py::pure_virtual(&IPluginModPage::handlesDownload), + // (py::arg("page_url"), "download_url", "fileinfo")) + // .def("setParentWidget", &IPluginModPage::setParentWidget, + // &IPluginModPageWrapper::setParentWidget_Default, py::arg("parent")) + // .def("_parentWidget", &IPluginModPageWrapper::parentWidget, + // py::return_value_policy()) + // ; + + // py::class_, + // boost::noncopyable>("IPluginPreview") + // .def("localizedName", &MOBase::IPlugin::localizedName, + // &IPluginPreviewWrapper::localizedName_Default) .def("master", + // &MOBase::IPlugin::master, &IPluginPreviewWrapper::master_Default) + // .def("requirements", &MOBase::IPlugin::requirements, + // &IPluginPreviewWrapper::requirements_Default) + // .def("enabledByDefault", &MOBase::IPlugin::enabledByDefault, + // &IPluginPreviewWrapper::enabledByDefault_Default) + + // .def("supportedExtensions", + // py::pure_virtual(&IPluginPreview::supportedExtensions)) + // .def("genFilePreview", + // py::pure_virtual(&IPluginPreview::genFilePreview), + // py::return_value_policy(), + // (py::arg("filename"), "max_size")) + // ; + + // py::class_, + // boost::noncopyable>("IPluginTool") + // .def("localizedName", &MOBase::IPlugin::localizedName, + // &IPluginToolWrapper::localizedName_Default) .def("master", + // &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default) + // .def("requirements", &MOBase::IPlugin::requirements, + // &IPluginToolWrapper::requirements_Default) .def("enabledByDefault", + // &MOBase::IPlugin::enabledByDefault, + // &IPluginToolWrapper::enabledByDefault_Default) + + // .def("displayName", py::pure_virtual(&IPluginTool::displayName)) + // .def("tooltip", py::pure_virtual(&IPluginTool::tooltip)) + // .def("icon", py::pure_virtual(&IPluginTool::icon)) + // .def("display", py::pure_virtual(&IPluginTool::display)) + // .def("setParentWidget", &IPluginTool::setParentWidget, + // &IPluginToolWrapper::setParentWidget_Default, py::arg("parent")) + // .def("_parentWidget", &IPluginToolWrapper::parentWidget, + // py::return_value_policy()) + // ; + } + +} // namespace mo2::python diff --git a/src/runner-pybind11/pyfiletree.cpp b/src/runner-pybind11/wrappers/pyfiletree.cpp similarity index 87% rename from src/runner-pybind11/pyfiletree.cpp rename to src/runner-pybind11/wrappers/pyfiletree.cpp index 4bac405..35850e7 100644 --- a/src/runner-pybind11/pyfiletree.cpp +++ b/src/runner-pybind11/wrappers/pyfiletree.cpp @@ -5,13 +5,14 @@ #include #include +#include #include +#include "../pybind11_qt/pybind11_qt.h" + #include #include -#include "pybind11_qt/pybind11_qt.h" - namespace py = pybind11; using namespace MOBase; @@ -32,8 +33,7 @@ namespace mo2::details { std::shared_ptr addFile(QString name, bool) override { if (m_Callback && !m_Callback(name, false)) { - throw UnsupportedOperationException( - "File rejected by callback."); + throw UnsupportedOperationException("File rejected by callback."); } return IFileTree::addFile(name); } @@ -41,8 +41,7 @@ namespace mo2::details { std::shared_ptr addDirectory(QString name) override { if (m_Callback && !m_Callback(name, true)) { - throw UnsupportedOperationException( - "Directory rejected by callback."); + throw UnsupportedOperationException("Directory rejected by callback."); } return IFileTree::addDirectory(name); } @@ -55,9 +54,9 @@ namespace mo2::details { return std::make_shared(parent, name, m_Callback); } - bool doPopulate( - std::shared_ptr parent, - std::vector>& entries) const override + bool + doPopulate(std::shared_ptr parent, + std::vector>& entries) const override { return true; } @@ -75,9 +74,8 @@ namespace mo2::details { #pragma optimize("", off) namespace pybind11 { - const void* - polymorphic_type_hook::get(const FileTreeEntry* src, - const std::type_info*& type) + const void* polymorphic_type_hook::get(const FileTreeEntry* src, + const std::type_info*& type) { if (auto p = dynamic_cast(src)) { type = &typeid(IFileTree); @@ -94,8 +92,8 @@ namespace mo2::python { // FileTreeEntry Scope: auto fileTreeEntryClass = - py::class_>( - m, "FileTreeEntry"); + py::class_>(m, + "FileTreeEntry"); // we do not use the enum directly, we will mostly bind the FileTypes // (with an S) @@ -110,17 +108,15 @@ namespace mo2::python { [](py::object) { return FileTreeEntry::DIRECTORY; }) - .def_property_readonly_static( - "FILE_OR_DIRECTORY", - [](py::object) { - return FileTreeEntry::FILE_OR_DIRECTORY; - }) + .def_property_readonly_static("FILE_OR_DIRECTORY", + [](py::object) { + return FileTreeEntry::FILE_OR_DIRECTORY; + }) .def(py::self == py::self) .def(py::self != py::self) .def(py::self | py::self); - py::implicitly_convertible(); + py::implicitly_convertible(); fileTreeEntryClass .def_property_readonly_static("FILE", @@ -159,8 +155,7 @@ namespace mo2::python { return entry->hasSuffix(suffix); }, py::arg("suffix")) - .def("parent", py::overload_cast<>(&FileTreeEntry::parent), - "[optional]") + .def("parent", py::overload_cast<>(&FileTreeEntry::parent), "[optional]") .def("path", &FileTreeEntry::path, py::arg("sep") = "\\") .def("pathFrom", &FileTreeEntry::pathFrom, py::arg("tree"), py::arg("sep") = "\\") @@ -175,8 +170,7 @@ namespace mo2::python { return entry->compare(other) == 0; }) .def("__eq__", - [](const FileTreeEntry* entry, - std::shared_ptr other) { + [](const FileTreeEntry* entry, std::shared_ptr other) { return entry == other.get(); }) @@ -209,8 +203,7 @@ namespace mo2::python { py::arg("path"), py::arg("type") = IFileTree::FILE_OR_DIRECTORY); iFileTreeClass.def( - "find", - py::overload_cast(&IFileTree::find), + "find", py::overload_cast(&IFileTree::find), py::arg("path"), py::arg("type") = IFileTree::FILE_OR_DIRECTORY, "[optional]"); iFileTreeClass.def("pathTo", &IFileTree::pathTo, py::arg("entry"), @@ -254,12 +247,10 @@ namespace mo2::python { // and handling. iFileTreeClass.def( "merge", - [](IFileTree* p, std::shared_ptr other, - bool returnOverwrites) + [](IFileTree* p, std::shared_ptr other, bool returnOverwrites) -> std::variant { IFileTree::OverwritesType overwrites; - auto result = - p->merge(other, returnOverwrites ? &overwrites : nullptr); + auto result = p->merge(other, returnOverwrites ? &overwrites : nullptr); if (result == IFileTree::MERGE_FAILED) { throw std::logic_error("merge failed"); } @@ -295,9 +286,8 @@ namespace mo2::python { }, py::arg("entry")); - iFileTreeClass.def( - "move", &IFileTree::move, py::arg("entry"), py::arg("path"), - py::arg("policy") = IFileTree::InsertPolicy::FAIL_IF_EXISTS); + iFileTreeClass.def("move", &IFileTree::move, py::arg("entry"), py::arg("path"), + py::arg("policy") = IFileTree::InsertPolicy::FAIL_IF_EXISTS); iFileTreeClass.def( "copy", [](IFileTree* w, std::shared_ptr entry, QString path, @@ -312,8 +302,7 @@ namespace mo2::python { py::arg("insert_policy") = IFileTree::InsertPolicy::FAIL_IF_EXISTS); iFileTreeClass.def("clear", &IFileTree::clear); - iFileTreeClass.def("removeAll", &IFileTree::removeAll, - py::arg("names")); + iFileTreeClass.def("removeAll", &IFileTree::removeAll, py::arg("names")); iFileTreeClass.def("removeIf", &IFileTree::removeIf, py::arg("filter")); // Special methods: diff --git a/src/runner-pybind11/wrappers/pyfiletree.h b/src/runner-pybind11/wrappers/pyfiletree.h new file mode 100644 index 0000000..c0b3a83 --- /dev/null +++ b/src/runner-pybind11/wrappers/pyfiletree.h @@ -0,0 +1,36 @@ +#ifndef MO2_PYTHON_FILETREE_H +#define MO2_PYTHON_FILETREE_H + +#include + +#include + +#include "../pybind11_qt/pybind11_qt.h" + +namespace pybind11 { + template <> + struct polymorphic_type_hook { + static const void* get(const MOBase::FileTreeEntry* src, + const std::type_info*& type); + }; +} // namespace pybind11 + +namespace mo2::python { + + /** + * @brief Add bindings for FileTreeEntry andIFileTree to the given module. + * + * @param mobase Module to add the bindings to. + */ + void add_ifiletree_bindings(pybind11::module_& m); + + /** + * @brief Add makeTree() function to the given module, useful for debugging. + * + * @param mobase Module to add the function to. + */ + void add_make_tree_function(pybind11::module_& m); + +} // namespace mo2::python + +#endif diff --git a/src/runner-pybind11/wrappers/uibase_wrappers.h b/src/runner-pybind11/wrappers/uibase_wrappers.h deleted file mode 100644 index 72dd28d..0000000 --- a/src/runner-pybind11/wrappers/uibase_wrappers.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef PYTHON_WRAPPERS_UIBASE_H -#define PYTHON_WRAPPERS_UIBASE_H - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include "pybind11_qt/pybind11_qt.h" - -namespace mo2::python { - - // this can be extended in C++, so why not in Python - class PyPluginRequirement : public MOBase::IPluginRequirement { - public: - std::optional - check(MOBase::IOrganizer* organizer) const override - { - PYBIND11_OVERRIDE_PURE(std::optional, IPluginRequirement, - check, organizer); - }; - }; - - // this needs to be extendable in Python, so actually needs a wrapper - class PySaveGame : public MOBase::ISaveGame { - public: - QString getFilepath() const override - { - PYBIND11_OVERRIDE_PURE(QString, ISaveGame, getFilepath, ); - } - - QDateTime getCreationTime() const override - { - PYBIND11_OVERRIDE_PURE(QDateTime, ISaveGame, getCreationTime, ); - } - - QString getName() const override - { - PYBIND11_OVERRIDE_PURE(QString, ISaveGame, getName, ); - } - - QString getSaveGroupIdentifier() const override - { - PYBIND11_OVERRIDE_PURE(QString, ISaveGame, - getSaveGroupIdentifier, ); - } - - QStringList allFiles() const override - { - PYBIND11_OVERRIDE_PURE(QStringList, ISaveGame, allFiles, ); - } - }; - - class PySaveGameInfoWidget : public MOBase::ISaveGameInfoWidget { - public: - // Bring the constructor: - using ISaveGameInfoWidget::ISaveGameInfoWidget; - - void setSave(MOBase::ISaveGame const& save) override - { - PYBIND11_OVERRIDE_PURE(void, ISaveGameInfoWidget, setSave, &save); - } - }; - -} // namespace mo2::python - -#endif // UIBASEWRAPPERS_H diff --git a/src/runner-pybind11/wrappers/widgets.cpp b/src/runner-pybind11/wrappers/widgets.cpp new file mode 100644 index 0000000..aed9e11 --- /dev/null +++ b/src/runner-pybind11/wrappers/widgets.cpp @@ -0,0 +1,79 @@ +#include "wrappers.h" + +#include +#include +#include +#include + +#include "../pybind11_qt/pybind11_qt.h" + +#include + +namespace py = pybind11; +using namespace MOBase; + +namespace mo2::python { + + void add_widget_bindings(pybind11::module_ m) + { + // TaskDialog is also in Windows System. + using TaskDialog = MOBase::TaskDialog; + + // TaskDialog + py::class_(m, "TaskDialogButton") + .def(py::init(), + py::arg("text"), py::arg("description"), py::arg("button")) + .def(py::init(), py::arg("text"), + py::arg("button")); + + py::class_(m, "TaskDialog") + .def(py::init([](QWidget* parent, QString const& title, QString const& main, + QString const& content, QString const& details, + QMessageBox::Icon icon, + std::vector const& buttons, + std::variant> const& + remember) { + auto* dialog = new TaskDialog(parent, title); + dialog->main(main).content(content).details(details).icon(icon); + + for (auto& button : buttons) { + dialog->button(button); + } + + std::visit( + [dialog](auto const& item) { + QString action, file; + if constexpr (std::is_same_v, + QString>) { + action = item; + } + else { + action = std::get<0>(item); + file = std::get<1>(item); + } + dialog->remember(action, file); + }, + remember); + + return dialog; + }), + py::return_value_policy::take_ownership, + py::arg("parent") = static_cast(nullptr), + py::arg("title") = "", py::arg("main") = "", py::arg("content") = "", + py::arg("details") = "", py::arg("icon") = QMessageBox::NoIcon, + py::arg("buttons") = std::vector{}, + py::arg("remember") = "") + .def("setTitle", &TaskDialog::title, py::arg("title")) + .def("setMain", &TaskDialog::main, py::arg("main")) + .def("setContent", &TaskDialog::content, py::arg("content")) + .def("setDetails", &TaskDialog::details, py::arg("details")) + .def("setIcon", &TaskDialog::icon, py::arg("icon")) + .def("addButton", &TaskDialog::button, py::arg("button")) + .def("setRemember", &TaskDialog::remember, py::arg("action"), + py::arg("file") = "") + .def("setWidth", &TaskDialog::setWidth, py::arg("widget")) + .def("addContent", &TaskDialog::addContent, py::arg("widget")) + .def("exec", &TaskDialog::exec); + } + +} // namespace mo2::python diff --git a/src/runner-pybind11/wrappers/wrappers.cpp b/src/runner-pybind11/wrappers/wrappers.cpp new file mode 100644 index 0000000..fff4a7d --- /dev/null +++ b/src/runner-pybind11/wrappers/wrappers.cpp @@ -0,0 +1,115 @@ + +#include "wrappers.h" + +#include +#include +#include +#include + +#include "../pybind11_qt/pybind11_qt.h" + +#include +#include +#include +#include + +// IOrganizer must be bring here to properly compile the Python bindings of +// plugin requirements +#include +#include +#include +#include + +using namespace MOBase; + +namespace mo2::python { + + // this can be extended in C++, so why not in Python + class PyPluginRequirement : public IPluginRequirement { + public: + std::optional check(IOrganizer* organizer) const override + { + PYBIND11_OVERRIDE_PURE(std::optional, IPluginRequirement, check, + organizer); + }; + }; + + // this needs to be extendable in Python, so actually needs a wrapper + class PySaveGame : public ISaveGame { + public: + QString getFilepath() const override + { + PYBIND11_OVERRIDE_PURE(QString, ISaveGame, getFilepath, ); + } + + QDateTime getCreationTime() const override + { + PYBIND11_OVERRIDE_PURE(QDateTime, ISaveGame, getCreationTime, ); + } + + QString getName() const override + { + PYBIND11_OVERRIDE_PURE(QString, ISaveGame, getName, ); + } + + QString getSaveGroupIdentifier() const override + { + PYBIND11_OVERRIDE_PURE(QString, ISaveGame, getSaveGroupIdentifier, ); + } + + QStringList allFiles() const override + { + PYBIND11_OVERRIDE_PURE(QStringList, ISaveGame, allFiles, ); + } + }; + + class PySaveGameInfoWidget : public ISaveGameInfoWidget { + public: + // Bring the constructor: + using ISaveGameInfoWidget::ISaveGameInfoWidget; + + void setSave(ISaveGame const& save) override + { + PYBIND11_OVERRIDE_PURE(void, ISaveGameInfoWidget, setSave, &save); + } + }; + + void add_wrapper_bindings(pybind11::module_ m) + { + namespace py = pybind11; + + // ISaveGame + // + py::class_(m, "ISaveGame") + .def(py::init<>()) + .def("getFilepath", &ISaveGame::getFilepath) + .def("getCreationTime", &ISaveGame::getCreationTime) + .def("getName", &ISaveGame::getName) + .def("getSaveGroupIdentifier", &ISaveGame::getSaveGroupIdentifier) + .def("allFiles", &ISaveGame::allFiles); + + // ISaveGameInfoWidget + // + py::class_ iSaveGameInfoWidget( + m, "ISaveGameInfoWidget"); + iSaveGameInfoWidget.def(py::init<>()) + .def(py::init(), py::arg("parent")) + .def("setSave", &ISaveGameInfoWidget::setSave, py::arg("save")); + py::qt::add_qt_delegate(iSaveGameInfoWidget, "widget"); + + // IPluginRequirement + // + py::class_ iPluginRequirementClass( + m, "IPluginRequirement"); + + py::class_(iPluginRequirementClass, "Problem") + .def(py::init(), py::arg("short_description"), + py::arg("long_description") = "") + .def("shortDescription", &IPluginRequirement::Problem::shortDescription) + .def("longDescription", &IPluginRequirement::Problem::longDescription); + + iPluginRequirementClass.def("check", &IPluginRequirement::check, + py::arg("organizer")); + } + +} // namespace mo2::python diff --git a/src/runner-pybind11/wrappers/wrappers.h b/src/runner-pybind11/wrappers/wrappers.h new file mode 100644 index 0000000..709d0e8 --- /dev/null +++ b/src/runner-pybind11/wrappers/wrappers.h @@ -0,0 +1,51 @@ +#ifndef PYTHON_WRAPPERS_WRAPPERS_H +#define PYTHON_WRAPPERS_WRAPPERS_H + +#include + +namespace mo2::python { + + /** + * @brief Add bindings for the various classes in uibase that are not + * wrappers (i.e., cannot be extended from Python). + * + * @param m Python module to add bindings to. + */ + void add_basic_bindings(pybind11::module_ m); + + /** + * @brief Add bindings for the various custom widget classes in uibase that + * cannot be extended from Python. + * + * @param m Python module to add bindings to. + */ + void add_widget_bindings(pybind11::module_ m); + + /** + * @brief Add bindings for the uibase wrappers to the given module. uibase + * wrappers include classes from uibase that can be extended from Python but + * are neither plugins nor game features (e.g., ISaveGame). + * + * @param m Python module to add bindings to. + */ + void add_wrapper_bindings(pybind11::module_ m); + + /** + * @brief Add bindings for the various plugin classes in uibase that can be + * extended from Python. + * + * @param m Python module to add bindings to. + */ + void add_plugins_bindings(pybind11::module_ m); + + /** + * @brief Add bindings for the various game features classes in uibase that + * can be extended from Python. + * + * @param m Python module to add bindings to. + */ + void add_game_feature_bindings(pybind11::module_ m); + +} // namespace mo2::python + +#endif // PYTHON_WRAPPERS_WRAPPERS_H