diff --git a/cmd/genbindings/config-allowlist.go b/cmd/genbindings/config-allowlist.go index 7686338c..92bf5fdd 100644 --- a/cmd/genbindings/config-allowlist.go +++ b/cmd/genbindings/config-allowlist.go @@ -651,6 +651,10 @@ func ApplyQuirks(className string, mm *CppMethod) { mm.BecomesNonConstInVersion = addr("6.7") } + if className == "QObjectData" && mm.MethodName == "dynamicMetaObject" { + mm.ReturnType.BecomesConstInVersion = addr("6.9") + } + if className == "QFileDialog" && mm.MethodName == "saveFileContent" && mm.IsStatic { // The prototype was changed from // [Qt 5 - 6.6] void QFileDialog::saveFileContent(const QByteArray &fileContent, const QString &fileNameHint = QString()) diff --git a/cmd/genbindings/emitcabi.go b/cmd/genbindings/emitcabi.go index 7a42c024..98b1921e 100644 --- a/cmd/genbindings/emitcabi.go +++ b/cmd/genbindings/emitcabi.go @@ -790,6 +790,14 @@ extern "C" { `) + // We need this macro for QObjectData::dynamicMetaObject for Qt 6.9 + if filename == "qobject.h" && packageName == "qt6" { + ret.WriteString("// Based on the macro from Qt (LGPL v3), see https://www.qt.io/qt-licensing\n" + + "// Macro is trivial and used here under fair use\n" + + "// Usage does not imply derivation\n" + + "#define QT_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch))\n\n") + } + foundTypesList := getReferencedTypes(src) ret.WriteString("#ifdef __cplusplus\n") @@ -852,7 +860,16 @@ extern "C" { continue // Can't call directly, have to go through our wrapper } - ret.WriteString(fmt.Sprintf("%s %s(%s);\n", m.ReturnType.RenderTypeCabi(), cabiMethodName(c, m), emitParametersCabi(m, ifv(m.IsConst, "const ", "")+className+"*"))) + if m.ReturnType.BecomesConstInVersion != nil && packageName == "qt6" { + ret.WriteString(fmt.Sprintf("// This method's return type was changed from non-const to const in Qt "+*m.ReturnType.BecomesConstInVersion) + "\n" + + "#if QT_VERSION >= QT_VERSION_CHECK(" + strings.Replace(*m.ReturnType.BecomesConstInVersion, `.`, `,`, -1) + ",0)\n" + + fmt.Sprintf("%s %s(%s);\n", "const "+m.ReturnType.RenderTypeCabi(), cabiMethodName(c, m), emitParametersCabi(m, ifv(m.IsConst, "const ", "")+className+"*")) + + "#else\n" + + fmt.Sprintf("%s %s(%s);\n", m.ReturnType.RenderTypeCabi(), cabiMethodName(c, m), emitParametersCabi(m, ifv(m.IsConst, "const ", "")+className+"*")) + + "#endif\n") + } else { + ret.WriteString(fmt.Sprintf("%s %s(%s);\n", m.ReturnType.RenderTypeCabi(), cabiMethodName(c, m), emitParametersCabi(m, ifv(m.IsConst, "const ", "")+className+"*"))) + } if m.IsSignal { ret.WriteString(fmt.Sprintf("%s %s(%s* self, intptr_t slot);\n", m.ReturnType.RenderTypeCabi(), cabiConnectName(c, m), className)) @@ -1249,6 +1266,20 @@ extern "C" { "\n", ) + } else if m.ReturnType.BecomesConstInVersion != nil && strings.Contains(src.Filename, "qt6") { + + ret.WriteString("" + + "// This method's return type was changed from non-const to const in Qt " + *m.ReturnType.BecomesConstInVersion + "\n" + + "#if QT_VERSION >= QT_VERSION_CHECK(" + strings.Replace(*m.ReturnType.BecomesConstInVersion, `.`, `,`, -1) + ",0)\n" + + "const " + m.ReturnType.RenderTypeCabi() + " " + methodPrefixName + "_" + m.SafeMethodName() + "(" + emitParametersCabi(m, ifv(m.IsConst, "const ", "")+methodPrefixName+"*") + ") {\n" + + "#else\n" + + m.ReturnType.RenderTypeCabi() + " " + methodPrefixName + "_" + m.SafeMethodName() + "(" + emitParametersCabi(m, ifv(m.IsConst, "const ", "")+methodPrefixName+"*") + ") {\n" + + "#endif\n" + + preamble + "\n" + + emitAssignCppToCabi("\treturn ", m.ReturnType, callTarget) + + "}\n\n", + ) + } else { ret.WriteString(fmt.Sprintf( diff --git a/cmd/genbindings/intermediate.go b/cmd/genbindings/intermediate.go index 52c6edf0..6d8432d0 100644 --- a/cmd/genbindings/intermediate.go +++ b/cmd/genbindings/intermediate.go @@ -16,7 +16,8 @@ type CppParameter struct { Optional bool NoExcept bool - QtCppOriginalType *CppParameter // If we rewrote QStringList->QList, this field contains the original QStringList. Otherwise, it's blank + QtCppOriginalType *CppParameter // If we rewrote QStringList->QList, this field contains the original QStringList. Otherwise, it's blank + BecomesConstInVersion *string // "6,9" } func (p CppParameter) String() string { diff --git a/qt6/gen_qobject.cpp b/qt6/gen_qobject.cpp index 7c2a5635..0c6c756f 100644 --- a/qt6/gen_qobject.cpp +++ b/qt6/gen_qobject.cpp @@ -36,7 +36,13 @@ void miqt_exec_callback_QObject_disconnectNotify(QObject*, intptr_t, QMetaMethod } /* extern C */ #endif +// This method's return type was changed from non-const to const in Qt 6.9 +#if QT_VERSION >= QT_VERSION_CHECK(6,9,0) +const QMetaObject* QObjectData_dynamicMetaObject(const QObjectData* self) { +#else QMetaObject* QObjectData_dynamicMetaObject(const QObjectData* self) { +#endif + return self->dynamicMetaObject(); } diff --git a/qt6/gen_qobject.h b/qt6/gen_qobject.h index 47606a20..c9e4e0c3 100644 --- a/qt6/gen_qobject.h +++ b/qt6/gen_qobject.h @@ -14,6 +14,11 @@ extern "C" { #endif +// Based on the macro from Qt (LGPL v3), see https://www.qt.io/qt-licensing +// Macro is trivial and used here under fair use +// Usage does not imply derivation +#define QT_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch)) + #ifdef __cplusplus class QAnyStringView; class QBindingStorage; @@ -48,7 +53,12 @@ typedef struct QTimerEvent QTimerEvent; typedef struct QVariant QVariant; #endif +// This method's return type was changed from non-const to const in Qt 6.9 +#if QT_VERSION >= QT_VERSION_CHECK(6,9,0) +const QMetaObject* QObjectData_dynamicMetaObject(const QObjectData* self); +#else QMetaObject* QObjectData_dynamicMetaObject(const QObjectData* self); +#endif void QObjectData_delete(QObjectData* self); QObject* QObject_new();