From e06ce2eb62bc02824669433c5952c193943e0a7e Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Mon, 2 Mar 2020 16:09:06 +0100 Subject: Fix regression when doing a debug only build with MSVC qt helper libs for msvc will always get a 'd' suffix for debug builds. This is different than for MinGW, where it will only get a 'd' for mixed debug builds in -debug-and-release scenarios (see commit 1749f9184b97). This amends d32a679, which incorrectly removed the 'd' suffix when linking helper libs built for MSVC. Fixes: QTBUG-82620 Change-Id: I8097de0e1bab4d1e58fc37b0c50ee6b07650a626 Reviewed-by: Joerg Bornemann --- mkspecs/features/qt_helper_lib.prf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkspecs/features/qt_helper_lib.prf b/mkspecs/features/qt_helper_lib.prf index 8a9672e603..2bb01515be 100644 --- a/mkspecs/features/qt_helper_lib.prf +++ b/mkspecs/features/qt_helper_lib.prf @@ -60,7 +60,7 @@ win32|CONFIG(static, static|shared) { "QMAKE_DEFINES_$${ucmodule} = $$val_escape(MODULE_DEFINES)" android { MODULE_PRI_CONT += "QMAKE_LIBS_$${ucmodule} =" - } else: qtConfig(debug_and_release): { + } else: if(msvc|qtConfig(debug_and_release)): { win32: \ MODULE_DEBUG_LIBS = $$DESTDIR/$$prefix$${TARGET}d.$$suffix else: darwin: \ -- cgit v1.2.3 From bf059f61338b45e974786d6300f522b3e72a7faa Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Mon, 2 Mar 2020 14:33:07 +0100 Subject: Android: Include the resConfigs so that the package can be uploaded The Google Play Store requires the resConfigs to be set to something valid otherwise it will not accept the package, so we default it to "en" to ensure it is valid. Fixes: QTBUG-81735 Change-Id: I1180481a1e5b88057aed2417716ca4d334080c00 Reviewed-by: BogDan Vatra --- src/android/templates/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/android/templates/build.gradle b/src/android/templates/build.gradle index 3087d08c83..171fe0b996 100644 --- a/src/android/templates/build.gradle +++ b/src/android/templates/build.gradle @@ -59,4 +59,8 @@ android { aaptOptions { noCompress 'rcc' } + + defaultConfig { + resConfigs "en" + } } -- cgit v1.2.3 From 6806cb341ca5f21d0136f890ccd317d71aa3cab6 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Mon, 2 Mar 2020 14:27:45 +0200 Subject: Android: fix apk generation on Android API 23+ Starting with Android API 23+ gradle enables uncompressed native libs by default. We must set android.bundle.enableUncompressedNativeLibs = false to gradle.properties, to force it to compress them and extract them on the device. Fixes: QTBUG-80766 Change-Id: Ia6d8d9179a341bbe7f8dc254a3b31d2ee8d7a5d7 Reviewed-by: Andy Shaw --- src/tools/androiddeployqt/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index 3d378024c9..80612d34ac 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -2295,6 +2295,7 @@ bool buildAndroidProject(const Options &options) QString gradlePropertiesPath = options.outputDirectory + QLatin1String("gradle.properties"); GradleProperties gradleProperties = readGradleProperties(gradlePropertiesPath); + gradleProperties["android.bundle.enableUncompressedNativeLibs"] = "false"; gradleProperties["buildDir"] = "build"; gradleProperties["qt5AndroidDir"] = (options.qtInstallDirectory + QLatin1String("/src/android/java")).toUtf8(); gradleProperties["androidCompileSdkVersion"] = options.androidPlatform.split(QLatin1Char('-')).last().toLocal8Bit(); -- cgit v1.2.3 From 2f52afda8e77429c00029f94e887ed37dfb4e584 Mon Sep 17 00:00:00 2001 From: Kirill Burtsev Date: Tue, 3 Mar 2020 17:26:53 +0100 Subject: QLibraryInfo: avoid unneeded conversion for path prefix Ammends 4ac872639e. Change return type for getPrefix to eliminate toLatin1/fromLocal8bit conversion of initial value for relocatable prefix to preserve not-latin1 characters and return valid prefix path. Fixes: QTBUG-81462 Change-Id: I15cfa49e9e440e257b04dd31803cd1478f3b07f5 Reviewed-by: Alexandru Croitor Reviewed-by: Joerg Bornemann --- src/corelib/global/qlibraryinfo.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp index 7d6beaf9c2..acc73389ed 100644 --- a/src/corelib/global/qlibraryinfo.cpp +++ b/src/corelib/global/qlibraryinfo.cpp @@ -661,7 +661,7 @@ static QString getHostPrefixFromHostBinDir() #endif #ifndef QT_BUILD_QMAKE_BOOTSTRAP -static const char *getPrefix( +static QString getPrefix( #ifdef QT_BUILD_QMAKE QLibraryInfo::PathGroup group #endif @@ -670,15 +670,13 @@ static const char *getPrefix( #if defined(QT_BUILD_QMAKE) # if QT_CONFIGURE_CROSSBUILD if (group == QLibraryInfo::DevicePaths) - return QT_CONFIGURE_PREFIX_PATH; + return QString::fromLocal8Bit(QT_CONFIGURE_PREFIX_PATH); # endif - static QByteArray extPrefixPath = getExtPrefixFromHostBinDir().toLatin1(); - return extPrefixPath.constData(); + return getExtPrefixFromHostBinDir(); #elif QT_CONFIG(relocatable) - static QByteArray prefixPath = getRelocatablePrefix().toLatin1(); - return prefixPath.constData(); + return getRelocatablePrefix(); #else - return QT_CONFIGURE_PREFIX_PATH; + return QString::fromLocal8Bit(QT_CONFIGURE_PREFIX_PATH); #endif } #endif // QT_BUILD_QMAKE_BOOTSTRAP @@ -794,7 +792,7 @@ QLibraryInfo::rawLocation(LibraryLocation loc, PathGroup group) if (!fromConf) { const char * volatile path = 0; if (loc == PrefixPath) { - path = getPrefix( + ret = getPrefix( #ifdef QT_BUILD_QMAKE group #endif -- cgit v1.2.3 From 2c1b4e37b936f64d6b52e2bc10ff97184a714b9a Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 4 Mar 2020 07:44:22 +0100 Subject: Fix CVE-2020-9327 in SQLite This was taken from abc473fb8fb99900 in SQLite, ref: https://www.sqlite.org/cgi/src/info/abc473fb8fb99900 Fixes: QTBUG-82533 Change-Id: I9840e29f19a0b861229987f5b59d8585ba2e55dc Reviewed-by: Simon Hausmann --- .../patches/0001-Fix-CVE-2020-9327-in-SQLite.patch | 203 +++++++++++++++++++++ src/3rdparty/sqlite/sqlite3.c | 31 +++- 2 files changed, 225 insertions(+), 9 deletions(-) create mode 100644 src/3rdparty/sqlite/patches/0001-Fix-CVE-2020-9327-in-SQLite.patch diff --git a/src/3rdparty/sqlite/patches/0001-Fix-CVE-2020-9327-in-SQLite.patch b/src/3rdparty/sqlite/patches/0001-Fix-CVE-2020-9327-in-SQLite.patch new file mode 100644 index 0000000000..4fbb2ee339 --- /dev/null +++ b/src/3rdparty/sqlite/patches/0001-Fix-CVE-2020-9327-in-SQLite.patch @@ -0,0 +1,203 @@ +From 63566d1fff2665b777650594eec6eefd3587e177 Mon Sep 17 00:00:00 2001 +From: Andy Shaw +Date: Wed, 4 Mar 2020 07:44:22 +0100 +Subject: [PATCH] Fix CVE-2020-9327 in SQLite + +This was taken from abc473fb8fb99900 in SQLite, ref: +https://www.sqlite.org/cgi/src/info/abc473fb8fb99900 + +Fixes: QTBUG-82533 +Change-Id: I9840e29f19a0b861229987f5b59d8585ba2e55dc +--- + .../0001-Fix-CVE-2020-9327-in-SQLite.patch | 96 +++++++++++++++++++ + src/3rdparty/sqlite/sqlite3.c | 31 ++++-- + 2 files changed, 118 insertions(+), 9 deletions(-) + create mode 100644 src/3rdparty/sqlite/patches/0001-Fix-CVE-2020-9327-in-SQLite.patch + +diff --git a/src/3rdparty/sqlite/patches/0001-Fix-CVE-2020-9327-in-SQLite.patch b/src/3rdparty/sqlite/patches/0001-Fix-CVE-2020-9327-in-SQLite.patch +new file mode 100644 +index 0000000000..e0e8206db5 +--- /dev/null ++++ b/src/3rdparty/sqlite/patches/0001-Fix-CVE-2020-9327-in-SQLite.patch +@@ -0,0 +1,96 @@ ++From f79860e0fe251e3267a3cd5558dce98f918e0caa Mon Sep 17 00:00:00 2001 ++From: Andy Shaw ++Date: Wed, 4 Mar 2020 07:44:22 +0100 ++Subject: [PATCH] Fix CVE-2020-9327 in SQLite ++ ++Fixes: QTBUG-82533 ++Change-Id: I9840e29f19a0b861229987f5b59d8585ba2e55dc ++--- ++ src/3rdparty/sqlite/sqlite3.c | 31 ++++++++++++++++++++++--------- ++ 1 file changed, 22 insertions(+), 9 deletions(-) ++ ++diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c ++index 55dc686ee0..dfe5323a59 100644 ++--- a/src/3rdparty/sqlite/sqlite3.c +++++ b/src/3rdparty/sqlite/sqlite3.c ++@@ -17428,8 +17428,11 @@ struct Table { ++ */ ++ #ifndef SQLITE_OMIT_VIRTUALTABLE ++ # define IsVirtual(X) ((X)->nModuleArg) +++# define ExprIsVtab(X) \ +++ ((X)->op==TK_COLUMN && (X)->y.pTab!=0 && (X)->y.pTab->nModuleArg) ++ #else ++ # define IsVirtual(X) 0 +++# define ExprIsVtab(X) 0 ++ #endif ++ ++ /* ++@@ -104133,19 +104136,25 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ ++ case TK_LT: ++ case TK_LE: ++ case TK_GT: ++- case TK_GE: +++ case TK_GE: { +++ Expr *pLeft = pExpr->pLeft; +++ Expr *pRight = pExpr->pRight; ++ testcase( pExpr->op==TK_EQ ); ++ testcase( pExpr->op==TK_NE ); ++ testcase( pExpr->op==TK_LT ); ++ testcase( pExpr->op==TK_LE ); ++ testcase( pExpr->op==TK_GT ); ++ testcase( pExpr->op==TK_GE ); ++- if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->y.pTab)) ++- || (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->y.pTab)) +++ /* The y.pTab=0 assignment in wherecode.c always happens after the +++ ** impliesNotNullRow() test */ +++ if( (pLeft->op==TK_COLUMN && ALWAYS(pLeft->y.pTab!=0) +++ && IsVirtual(pLeft->y.pTab)) +++ || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) +++ && IsVirtual(pRight->y.pTab)) ++ ){ ++- return WRC_Prune; +++ return WRC_Prune; ++ } ++- +++ } ++ default: ++ return WRC_Continue; ++ } ++@@ -142591,7 +142600,8 @@ static int isAuxiliaryVtabOperator( ++ ** MATCH(expression,vtab_column) ++ */ ++ pCol = pList->a[1].pExpr; ++- if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){ +++ testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); +++ if( ExprIsVtab(pCol) ){ ++ for(i=0; iu.zToken, aOp[i].zOp)==0 ){ ++ *peOp2 = aOp[i].eOp2; ++@@ -142613,7 +142623,8 @@ static int isAuxiliaryVtabOperator( ++ ** with function names in an arbitrary case. ++ */ ++ pCol = pList->a[0].pExpr; ++- if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){ +++ testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); +++ if( ExprIsVtab(pCol) ){ ++ sqlite3_vtab *pVtab; ++ sqlite3_module *pMod; ++ void (*xNotUsed)(sqlite3_context*,int,sqlite3_value**); ++@@ -142636,10 +142647,12 @@ static int isAuxiliaryVtabOperator( ++ int res = 0; ++ Expr *pLeft = pExpr->pLeft; ++ Expr *pRight = pExpr->pRight; ++- if( pLeft->op==TK_COLUMN && IsVirtual(pLeft->y.pTab) ){ +++ testcase( pLeft->op==TK_COLUMN && pLeft->y.pTab==0 ); +++ if( ExprIsVtab(pLeft) ){ ++ res++; ++ } ++- if( pRight && pRight->op==TK_COLUMN && IsVirtual(pRight->y.pTab) ){ +++ testcase( pRight && pRight->op==TK_COLUMN && pRight->y.pTab==0 ); +++ if( pRight && ExprIsVtab(pRight) ){ ++ res++; ++ SWAP(Expr*, pLeft, pRight); ++ } ++-- ++2.21.0 (Apple Git-122.2) ++ +diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c +index 55dc686ee0..dfe5323a59 100644 +--- a/src/3rdparty/sqlite/sqlite3.c ++++ b/src/3rdparty/sqlite/sqlite3.c +@@ -17428,8 +17428,11 @@ struct Table { + */ + #ifndef SQLITE_OMIT_VIRTUALTABLE + # define IsVirtual(X) ((X)->nModuleArg) ++# define ExprIsVtab(X) \ ++ ((X)->op==TK_COLUMN && (X)->y.pTab!=0 && (X)->y.pTab->nModuleArg) + #else + # define IsVirtual(X) 0 ++# define ExprIsVtab(X) 0 + #endif + + /* +@@ -104133,19 +104136,25 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ + case TK_LT: + case TK_LE: + case TK_GT: +- case TK_GE: ++ case TK_GE: { ++ Expr *pLeft = pExpr->pLeft; ++ Expr *pRight = pExpr->pRight; + testcase( pExpr->op==TK_EQ ); + testcase( pExpr->op==TK_NE ); + testcase( pExpr->op==TK_LT ); + testcase( pExpr->op==TK_LE ); + testcase( pExpr->op==TK_GT ); + testcase( pExpr->op==TK_GE ); +- if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->y.pTab)) +- || (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->y.pTab)) ++ /* The y.pTab=0 assignment in wherecode.c always happens after the ++ ** impliesNotNullRow() test */ ++ if( (pLeft->op==TK_COLUMN && ALWAYS(pLeft->y.pTab!=0) ++ && IsVirtual(pLeft->y.pTab)) ++ || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) ++ && IsVirtual(pRight->y.pTab)) + ){ +- return WRC_Prune; ++ return WRC_Prune; + } +- ++ } + default: + return WRC_Continue; + } +@@ -142591,7 +142600,8 @@ static int isAuxiliaryVtabOperator( + ** MATCH(expression,vtab_column) + */ + pCol = pList->a[1].pExpr; +- if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){ ++ testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); ++ if( ExprIsVtab(pCol) ){ + for(i=0; iu.zToken, aOp[i].zOp)==0 ){ + *peOp2 = aOp[i].eOp2; +@@ -142613,7 +142623,8 @@ static int isAuxiliaryVtabOperator( + ** with function names in an arbitrary case. + */ + pCol = pList->a[0].pExpr; +- if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){ ++ testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); ++ if( ExprIsVtab(pCol) ){ + sqlite3_vtab *pVtab; + sqlite3_module *pMod; + void (*xNotUsed)(sqlite3_context*,int,sqlite3_value**); +@@ -142636,10 +142647,12 @@ static int isAuxiliaryVtabOperator( + int res = 0; + Expr *pLeft = pExpr->pLeft; + Expr *pRight = pExpr->pRight; +- if( pLeft->op==TK_COLUMN && IsVirtual(pLeft->y.pTab) ){ ++ testcase( pLeft->op==TK_COLUMN && pLeft->y.pTab==0 ); ++ if( ExprIsVtab(pLeft) ){ + res++; + } +- if( pRight && pRight->op==TK_COLUMN && IsVirtual(pRight->y.pTab) ){ ++ testcase( pRight && pRight->op==TK_COLUMN && pRight->y.pTab==0 ); ++ if( pRight && ExprIsVtab(pRight) ){ + res++; + SWAP(Expr*, pLeft, pRight); + } +-- +2.21.0 (Apple Git-122.2) + diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c index 55dc686ee0..dfe5323a59 100644 --- a/src/3rdparty/sqlite/sqlite3.c +++ b/src/3rdparty/sqlite/sqlite3.c @@ -17428,8 +17428,11 @@ struct Table { */ #ifndef SQLITE_OMIT_VIRTUALTABLE # define IsVirtual(X) ((X)->nModuleArg) +# define ExprIsVtab(X) \ + ((X)->op==TK_COLUMN && (X)->y.pTab!=0 && (X)->y.pTab->nModuleArg) #else # define IsVirtual(X) 0 +# define ExprIsVtab(X) 0 #endif /* @@ -104133,19 +104136,25 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ case TK_LT: case TK_LE: case TK_GT: - case TK_GE: + case TK_GE: { + Expr *pLeft = pExpr->pLeft; + Expr *pRight = pExpr->pRight; testcase( pExpr->op==TK_EQ ); testcase( pExpr->op==TK_NE ); testcase( pExpr->op==TK_LT ); testcase( pExpr->op==TK_LE ); testcase( pExpr->op==TK_GT ); testcase( pExpr->op==TK_GE ); - if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->y.pTab)) - || (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->y.pTab)) + /* The y.pTab=0 assignment in wherecode.c always happens after the + ** impliesNotNullRow() test */ + if( (pLeft->op==TK_COLUMN && ALWAYS(pLeft->y.pTab!=0) + && IsVirtual(pLeft->y.pTab)) + || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) + && IsVirtual(pRight->y.pTab)) ){ - return WRC_Prune; + return WRC_Prune; } - + } default: return WRC_Continue; } @@ -142591,7 +142600,8 @@ static int isAuxiliaryVtabOperator( ** MATCH(expression,vtab_column) */ pCol = pList->a[1].pExpr; - if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){ + testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); + if( ExprIsVtab(pCol) ){ for(i=0; iu.zToken, aOp[i].zOp)==0 ){ *peOp2 = aOp[i].eOp2; @@ -142613,7 +142623,8 @@ static int isAuxiliaryVtabOperator( ** with function names in an arbitrary case. */ pCol = pList->a[0].pExpr; - if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){ + testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); + if( ExprIsVtab(pCol) ){ sqlite3_vtab *pVtab; sqlite3_module *pMod; void (*xNotUsed)(sqlite3_context*,int,sqlite3_value**); @@ -142636,10 +142647,12 @@ static int isAuxiliaryVtabOperator( int res = 0; Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; - if( pLeft->op==TK_COLUMN && IsVirtual(pLeft->y.pTab) ){ + testcase( pLeft->op==TK_COLUMN && pLeft->y.pTab==0 ); + if( ExprIsVtab(pLeft) ){ res++; } - if( pRight && pRight->op==TK_COLUMN && IsVirtual(pRight->y.pTab) ){ + testcase( pRight && pRight->op==TK_COLUMN && pRight->y.pTab==0 ); + if( pRight && ExprIsVtab(pRight) ){ res++; SWAP(Expr*, pLeft, pRight); } -- cgit v1.2.3 From 7fcc4e0a57deb01de209ec8ff4d92c3a81e61fe9 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 6 Mar 2020 09:24:25 +0100 Subject: Windows QPA: Do not play sound for QMessageBox::NoIcon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amends 2f366a63b20a943ae3099605c2cdb34009ca5602. Task-number: QTBUG-82682 Task-number: QTBUG-81342 Change-Id: I30f465bf432e27828db460f6dbbb59eee0cca8f2 Reviewed-by: Oliver Wolff Reviewed-by: André de la Rocha --- src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp index 32a57473ad..fef5346eaf 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp @@ -105,6 +105,7 @@ static QString alertSound(const QObject *object) case Critical: return QStringLiteral("SystemHand"); } + return QString(); } return QStringLiteral("SystemAsterisk"); } -- cgit v1.2.3 From 1309205b8b5a39e9a5c6ca86f104d83095c3890d Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 19 Feb 2020 18:21:35 +0100 Subject: winrt: Update capability management to include IOT namespace Change-Id: Ib24ecb70454af5ab2e82ebbdc1fed9ef2a52bbb1 Reviewed-by: Oliver Wolff --- mkspecs/common/winrt_winphone/manifests/10.0/AppxManifest.xml.in | 3 ++- mkspecs/features/winrt/package_manifest.prf | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mkspecs/common/winrt_winphone/manifests/10.0/AppxManifest.xml.in b/mkspecs/common/winrt_winphone/manifests/10.0/AppxManifest.xml.in index 18683e01e3..fe9ddaf8f1 100644 --- a/mkspecs/common/winrt_winphone/manifests/10.0/AppxManifest.xml.in +++ b/mkspecs/common/winrt_winphone/manifests/10.0/AppxManifest.xml.in @@ -6,7 +6,8 @@ xmlns:uap=\"http://schemas.microsoft.com/appx/manifest/uap/windows10\" xmlns:uap3=\"http://schemas.microsoft.com/appx/manifest/uap/windows10/3\" xmlns:mobile=\"http://schemas.microsoft.com/appx/manifest/mobile/windows10\" - IgnorableNamespaces=\"uap uap3 mp mobile\"> + xmlns:iot=\"http://schemas.microsoft.com/appx/manifest/iot/windows10\" + IgnorableNamespaces=\"uap uap3 mp mobile iot\"> " else:contains(UAP3_CAPABILITIES, $$CAPABILITY): \ MANIFEST_CAPABILITIES += " " + else:contains(IOT_CAPABILITIES, $$CAPABILITY): \ + MANIFEST_CAPABILITIES += " " else: \ MANIFEST_CAPABILITIES += " " } -- cgit v1.2.3 From ddd0919bcf5bb630efce755cc21f364e36e23fca Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 11 Mar 2020 12:57:41 +0100 Subject: Do not crash on skipped Xlib visuals If XLIB_SKIP_ARGB_VISUALS is set, we might fail to find visuals for supported configurations. Task-number: QTBUG-81904 Change-Id: Ib0d26faabe430925881b7f2acfc5361df3af416b Reviewed-by: Laszlo Agocs --- src/platformsupport/glxconvenience/qglxconvenience.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platformsupport/glxconvenience/qglxconvenience.cpp b/src/platformsupport/glxconvenience/qglxconvenience.cpp index 81bccb1c25..e2f4922c8a 100644 --- a/src/platformsupport/glxconvenience/qglxconvenience.cpp +++ b/src/platformsupport/glxconvenience/qglxconvenience.cpp @@ -224,6 +224,8 @@ GLXFBConfig qglx_findConfig(Display *display, int screen , QSurfaceFormat format } QXlibPointer visual(glXGetVisualFromFBConfig(display, candidate)); + if (!visual) + continue; int actualRed; int actualGreen; int actualBlue; -- cgit v1.2.3 From c668fd940d0c610324254d5aa5aab6e0769f78a6 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 11 Mar 2020 10:38:20 +0100 Subject: Fix 'out of process' autotests We are, arguably, not testing QProcess and its ability to start or finish, we test QUdpSocket. If, for some reason (as we discovered on some specific machines recently) the process does not start or does not produce any output (canReadLine), we QSKIP instead of failing. Also, all those QCOMPARE will bypass the part there we stop processes - so must be RAII-protected. Fixes: QTBUG-82717 Change-Id: Idfb0d4a483d753f336b3827875eeaf51c79270e2 Reviewed-by: Volker Hilsheimer --- .../network/socket/qudpsocket/tst_qudpsocket.cpp | 82 ++++++++++++++-------- 1 file changed, 54 insertions(+), 28 deletions(-) diff --git a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp index 0f419e9de4..bed8a8b129 100644 --- a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp @@ -1090,12 +1090,21 @@ void tst_QUdpSocket::outOfProcessConnectedClientServerTest() QProcess serverProcess; serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"), QIODevice::ReadWrite | QIODevice::Text); - QVERIFY2(serverProcess.waitForStarted(3000), - qPrintable("Failed to start subprocess: " + serverProcess.errorString())); + + const auto serverProcessCleaner = qScopeGuard([&serverProcess] { + serverProcess.kill(); + serverProcess.waitForFinished(); + }); + + if (!serverProcess.waitForStarted(3000)) + QSKIP("Failed to start server as a subprocess"); // Wait until the server has started and reports success. - while (!serverProcess.canReadLine()) - QVERIFY(serverProcess.waitForReadyRead(3000)); + while (!serverProcess.canReadLine()) { + if (!serverProcess.waitForReadyRead(3000)) + QSKIP("No output from the server process, bailing out"); + } + QByteArray serverGreeting = serverProcess.readLine(); QVERIFY(serverGreeting != QByteArray("XXX\n")); int serverPort = serverGreeting.trimmed().toInt(); @@ -1105,12 +1114,21 @@ void tst_QUdpSocket::outOfProcessConnectedClientServerTest() clientProcess.start(QString::fromLatin1("clientserver/clientserver connectedclient %1 %2") .arg(QLatin1String("127.0.0.1")).arg(serverPort), QIODevice::ReadWrite | QIODevice::Text); - QVERIFY2(clientProcess.waitForStarted(3000), - qPrintable("Failed to start subprocess: " + clientProcess.errorString())); - // Wait until the server has started and reports success. - while (!clientProcess.canReadLine()) - QVERIFY(clientProcess.waitForReadyRead(3000)); + const auto clientProcessCleaner = qScopeGuard([&clientProcess] { + clientProcess.kill(); + clientProcess.waitForFinished(); + }); + + if (!clientProcess.waitForStarted(3000)) + QSKIP("Client process did not start"); + + // Wait until the client has started and reports success. + while (!clientProcess.canReadLine()) { + if (!clientProcess.waitForReadyRead(3000)) + QSKIP("No output from the client process, bailing out"); + } + QByteArray clientGreeting = clientProcess.readLine(); QCOMPARE(clientGreeting, QByteArray("ok\n")); @@ -1135,11 +1153,6 @@ void tst_QUdpSocket::outOfProcessConnectedClientServerTest() QCOMPARE(serverData.at(i * 3 + 2).trimmed().mid(8).toInt(), sdata.mid(4).trimmed().toInt() * 2); } - - clientProcess.kill(); - QVERIFY(clientProcess.waitForFinished()); - serverProcess.kill(); - QVERIFY(serverProcess.waitForFinished()); #endif } @@ -1151,12 +1164,21 @@ void tst_QUdpSocket::outOfProcessUnconnectedClientServerTest() QProcess serverProcess; serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"), QIODevice::ReadWrite | QIODevice::Text); - QVERIFY2(serverProcess.waitForStarted(3000), - qPrintable("Failed to start subprocess: " + serverProcess.errorString())); + + const auto serverProcessCleaner = qScopeGuard([&serverProcess] { + serverProcess.kill(); + serverProcess.waitForFinished(); + }); + + if (!serverProcess.waitForStarted(3000)) + QSKIP("Failed to start the server subprocess"); // Wait until the server has started and reports success. - while (!serverProcess.canReadLine()) - QVERIFY(serverProcess.waitForReadyRead(3000)); + while (!serverProcess.canReadLine()) { + if (!serverProcess.waitForReadyRead(3000)) + QSKIP("No output from the server, probably, it is not running"); + } + QByteArray serverGreeting = serverProcess.readLine(); QVERIFY(serverGreeting != QByteArray("XXX\n")); int serverPort = serverGreeting.trimmed().toInt(); @@ -1166,12 +1188,21 @@ void tst_QUdpSocket::outOfProcessUnconnectedClientServerTest() clientProcess.start(QString::fromLatin1("clientserver/clientserver unconnectedclient %1 %2") .arg(QLatin1String("127.0.0.1")).arg(serverPort), QIODevice::ReadWrite | QIODevice::Text); - QVERIFY2(clientProcess.waitForStarted(3000), - qPrintable("Failed to start subprocess: " + clientProcess.errorString())); - // Wait until the server has started and reports success. - while (!clientProcess.canReadLine()) - QVERIFY(clientProcess.waitForReadyRead(3000)); + const auto clientProcessCleaner = qScopeGuard([&clientProcess] { + clientProcess.kill(); + clientProcess.waitForFinished(); + }); + + if (!clientProcess.waitForStarted(3000)) + QSKIP("Failed to start the client's subprocess"); + + // Wait until the client has started and reports success. + while (!clientProcess.canReadLine()) { + if (!clientProcess.waitForReadyRead(3000)) + QSKIP("The client subprocess produced not output, exiting."); + } + QByteArray clientGreeting = clientProcess.readLine(); QCOMPARE(clientGreeting, QByteArray("ok\n")); @@ -1197,11 +1228,6 @@ void tst_QUdpSocket::outOfProcessUnconnectedClientServerTest() QCOMPARE(serverData.at(i * 3 + 2).trimmed().mid(8).toInt(), sdata.mid(4).trimmed().toInt() * 2); } - - clientProcess.kill(); - QVERIFY(clientProcess.waitForFinished()); - serverProcess.kill(); - QVERIFY(serverProcess.waitForFinished()); #endif } -- cgit v1.2.3 From 6b70c6b866e14145d013becff09c56a4aafe47e6 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Wed, 11 Mar 2020 13:59:39 +0100 Subject: ANGLE: Fix severe performance regression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The changed buffer usage priority that was introduced in our ANGLE update caused severe performance regressions for Qt applications. Fixes: QTBUG-73835 Change-Id: I49839bb272cdeec0027264f2751b88bc149665ad Reviewed-by: André de la Rocha Reviewed-by: Miguel Costa --- .../src/libANGLE/renderer/d3d/d3d11/Buffer11.h | 2 +- ...6-ANGLE-Fix-severe-performance-regression.patch | 37 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/angle/patches/0016-ANGLE-Fix-severe-performance-regression.patch diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h index ddbeeb90d2..f92a68454b 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h @@ -31,7 +31,6 @@ struct TranslatedAttribute; // The order of this enum governs priority of 'getLatestBufferStorage'. enum BufferUsage { - BUFFER_USAGE_SYSTEM_MEMORY, BUFFER_USAGE_STAGING, BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK, BUFFER_USAGE_INDEX, @@ -40,6 +39,7 @@ enum BufferUsage BUFFER_USAGE_PIXEL_UNPACK, BUFFER_USAGE_PIXEL_PACK, BUFFER_USAGE_UNIFORM, + BUFFER_USAGE_SYSTEM_MEMORY, BUFFER_USAGE_EMULATED_INDEXED_VERTEX, BUFFER_USAGE_COUNT, diff --git a/src/angle/patches/0016-ANGLE-Fix-severe-performance-regression.patch b/src/angle/patches/0016-ANGLE-Fix-severe-performance-regression.patch new file mode 100644 index 0000000000..e9cda1337f --- /dev/null +++ b/src/angle/patches/0016-ANGLE-Fix-severe-performance-regression.patch @@ -0,0 +1,37 @@ +From b215999d63d6e6b087e53e24a47b8b60520ec9e4 Mon Sep 17 00:00:00 2001 +From: Oliver Wolff +Date: Wed, 11 Mar 2020 13:59:39 +0100 +Subject: [PATCH] ANGLE: Fix severe performance regression + +The changed buffer usage priority that was introduced in our ANGLE +update caused severe performance regressions for Qt applications. + +Fixes: QTBUG-73835 +Change-Id: I49839bb272cdeec0027264f2751b88bc149665ad +--- + src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h +index ddbeeb90d2..f92a68454b 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h +@@ -31,7 +31,6 @@ struct TranslatedAttribute; + // The order of this enum governs priority of 'getLatestBufferStorage'. + enum BufferUsage + { +- BUFFER_USAGE_SYSTEM_MEMORY, + BUFFER_USAGE_STAGING, + BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK, + BUFFER_USAGE_INDEX, +@@ -40,6 +39,7 @@ enum BufferUsage + BUFFER_USAGE_PIXEL_UNPACK, + BUFFER_USAGE_PIXEL_PACK, + BUFFER_USAGE_UNIFORM, ++ BUFFER_USAGE_SYSTEM_MEMORY, + BUFFER_USAGE_EMULATED_INDEXED_VERTEX, + + BUFFER_USAGE_COUNT, +-- +2.20.1.windows.1 + -- cgit v1.2.3 From 476d296f42be63009cd06c8a6480176068535d00 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Sat, 15 Feb 2020 00:09:38 +0100 Subject: Fix QVarLengthArray documentation QVLA *does* have iterators and *can* be used with foreach (... I didn't say it should). Move its description together with the other containers. Change-Id: Ib60d1f7b3dc0e8c7004991bd4fdff95b3f23af60 Reviewed-by: Thiago Macieira --- src/corelib/doc/src/containers.qdoc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/corelib/doc/src/containers.qdoc b/src/corelib/doc/src/containers.qdoc index d0bb251e81..4c7cb32aac 100644 --- a/src/corelib/doc/src/containers.qdoc +++ b/src/corelib/doc/src/containers.qdoc @@ -123,6 +123,10 @@ a vector can be quite slow, because it can lead to large numbers of items having to be moved by one position in memory. + \row \li \l{QVarLengthArray} + \li This provides a low-level variable-length array. It can be used + instead of QVector in places where speed is particularly important. + \row \li \l{QStack} \li This is a convenience subclass of QVector that provides "last in, first out" (LIFO) semantics. It adds the following @@ -622,15 +626,11 @@ \section1 Other Container-Like Classes - Qt includes three template classes that resemble containers in + Qt includes other template classes that resemble containers in some respects. These classes don't provide iterators and cannot be used with the \c foreach keyword. \list - \li QVarLengthArray provides a low-level - variable-length array. It can be used instead of QVector in - places where speed is particularly important. - \li QCache provides a cache to store objects of a certain type T associated with keys of type Key. -- cgit v1.2.3 From 7d20f86958522bf1bdbaeaf8c0e395453c463058 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 11 Mar 2020 21:02:03 +0100 Subject: Stabilize and rename tst_qmessagebox::expandDetails_QTBUG_32473 This has been flaky on OpenSuSE; if the stored geom.topLeft() is 0,0 it apparently means the window manager (probably kwin) didn't get around to decorating and repositioning the dialog by the time qWaitForWindowExposed() returns. Because we check later to see whether it moved, we need to be certain of its initial position. Waiting for the extra "fleece" widget to be shown was based on the theory that by the time the X server has processed messages related to that new window, the WM should be done processing the consequences of the resized dialog window. But there's no such guarantee, so let's try removing that. On the other hand, removing the delay does open us up to miss a regression (maybe the dialog gets moved after we have checked that it didn't move). Rename because we don't name autotests after bugs. Amends 26ddb586acd49834c7cffac781ce504ec78635cc Task-number: QTBUG-32473 Change-Id: I6bbfe2b4baaee389db0d4112f0fec3b7cb9da554 Reviewed-by: Qt CI Bot Reviewed-by: Liang Qi --- .../auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp b/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp index 76314564f1..543128915e 100644 --- a/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp +++ b/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp @@ -51,7 +51,7 @@ private slots: void about(); void detailsText(); void detailsButtonText(); - void expandDetails_QTBUG_32473(); + void expandDetailsWithoutMoving(); #ifndef Q_OS_MAC void shortcut(); @@ -499,7 +499,7 @@ void tst_QMessageBox::detailsButtonText() } } -void tst_QMessageBox::expandDetails_QTBUG_32473() +void tst_QMessageBox::expandDetailsWithoutMoving() // QTBUG-32473 { tst_ResizingMessageBox box; box.setDetailedText("bla"); @@ -516,18 +516,14 @@ void tst_QMessageBox::expandDetails_QTBUG_32473() auto moreButton = *it; QVERIFY(QTest::qWaitForWindowExposed(&box)); + QTRY_VERIFY2(!box.geometry().topLeft().isNull(), "window manager is expected to decorate and position the dialog"); QRect geom = box.geometry(); box.resized = false; + // now click the "more" button, and verify that the dialog resizes but does not move moreButton->click(); QTRY_VERIFY(box.resized); - // After we receive the expose event for a second widget, it's likely - // that the window manager is also done manipulating the first QMessageBox. - QWidget fleece; - fleece.show(); - QVERIFY(QTest::qWaitForWindowExposed(&fleece)); - if (geom.topLeft() == box.geometry().topLeft()) - QTest::qWait(500); - QCOMPARE(geom.topLeft(), box.geometry().topLeft()); + QVERIFY(box.geometry().height() > geom.height()); + QCOMPARE(box.geometry().topLeft(), geom.topLeft()); } void tst_QMessageBox::incorrectDefaultButton() -- cgit v1.2.3 From bcdf49bcc6c357c9c30708e2e95ce4b8dbeb9f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Thu, 30 Jan 2020 13:43:03 +0100 Subject: wasm: support emsdk >= 1.39.4 Keep using the old/deprecated behavior for the Qt 5.14 series. Task-number: QTBUG-74601 Change-Id: Icee99803f300dfa0116a4de75f9fb26d1010625d Reviewed-by: Lorn Potter --- mkspecs/wasm-emscripten/qmake.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/mkspecs/wasm-emscripten/qmake.conf b/mkspecs/wasm-emscripten/qmake.conf index f4e9501415..beb08d643c 100644 --- a/mkspecs/wasm-emscripten/qmake.conf +++ b/mkspecs/wasm-emscripten/qmake.conf @@ -36,6 +36,7 @@ EMCC_COMMON_LFLAGS += \ -s NO_EXIT_RUNTIME=0 \ -s ERROR_ON_UNDEFINED_SYMBOLS=1 \ -s EXTRA_EXPORTED_RUNTIME_METHODS=[\"UTF16ToString\",\"stringToUTF16\"] \ + -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 \ --bind # The -s arguments can also be used with release builds, -- cgit v1.2.3 From d8ab719c0890195cfce0fb6d4c76b3664d6f3a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 10 Mar 2020 11:13:59 +0100 Subject: Fix double scaling of SVG icons on high DPI screens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On a high-dpi screen and AA_UseHighDpiPixmaps set, QIcon will ask its engine for a scaled-up pixmap. When the icon has been created from a theme, the engine is a QIconLoaderEngine. For a SVG icon, that engine would recursively use QIcon to load the scaled-up pixmap, leading to double scale-up. Fix by bypassing the QIcon API in the SVG case, loading the SVG icon directly from the SVG icon engine. Done-with: Tor Arne Vestbø Fixes: QTBUG-73587 Fixes: QTBUG-75039 Change-Id: I7fba02b6454decb5fcbca9c5a092e75954261dfd Reviewed-by: Eirik Aavitsland Reviewed-by: Tor Arne Vestbø --- src/gui/image/qiconloader.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp index 27c82bc09f..2f907a7709 100644 --- a/src/gui/image/qiconloader.cpp +++ b/src/gui/image/qiconloader.cpp @@ -797,8 +797,12 @@ QPixmap ScalableEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State if (svgIcon.isNull()) svgIcon = QIcon(filename); - // Simply reuse svg icon engine - return svgIcon.pixmap(size, mode, state); + // Bypass QIcon API, as that will scale by device pixel ratio of the + // highest DPR screen since we're not passing on any QWindow. + if (QIconEngine *engine = svgIcon.data_ptr() ? svgIcon.data_ptr()->engine : nullptr) + return engine->pixmap(size, mode, state); + + return QPixmap(); } QPixmap QIconLoaderEngine::pixmap(const QSize &size, QIcon::Mode mode, -- cgit v1.2.3 From 1d403ef81a2b9b19383f45b27d53149e122d65d8 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 9 Mar 2020 11:07:42 +0100 Subject: Windows QPA: Fix broken frame geometry when moving windows with native menus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QWindowsWindow::updateFullFrameMargins() which is called from the screen changed handling does not take native menus into account. Since the size of the menu is not known when using EnableNonClientDpiScaling(), obtaining the correct frame size requires triggering a WM_NCCALCSIZE message. Extract the helper forceNcCalcSize() from QWindowsMenu and use that from updateFullFrameMargins() in case menus are present. Amends d2fd9b1b9818b3ec88487967e010f66e92952f55. Fixes: QTBUG-82580 Change-Id: I306f1faf84e26c88608cb22ffd42eccc848905c3 Reviewed-by: André de la Rocha --- src/plugins/platforms/windows/qwindowscontext.cpp | 7 +++++++ src/plugins/platforms/windows/qwindowscontext.h | 2 ++ src/plugins/platforms/windows/qwindowsmenu.cpp | 11 ++--------- src/plugins/platforms/windows/qwindowswindow.cpp | 12 +++++++++++- src/plugins/platforms/windows/qwindowswindow.h | 1 + 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index d31352b854..293faf8a53 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -978,6 +978,13 @@ QByteArray QWindowsContext::comErrorString(HRESULT hr) return result; } +void QWindowsContext::forceNcCalcSize(HWND hwnd) +{ + // Force WM_NCCALCSIZE to adjust margin + SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, + SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); +} + bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out, unsigned dpi) { diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index 8027f09389..07398bd61c 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -246,6 +246,8 @@ public: bool asyncExpose() const; void setAsyncExpose(bool value); + static void forceNcCalcSize(HWND hwnd); + static bool systemParametersInfo(unsigned action, unsigned param, void *out, unsigned dpi = 0); static bool systemParametersInfoForScreen(unsigned action, unsigned param, void *out, const QPlatformScreen *screen = nullptr); diff --git a/src/plugins/platforms/windows/qwindowsmenu.cpp b/src/plugins/platforms/windows/qwindowsmenu.cpp index d20edd685e..221e4ff6ec 100644 --- a/src/plugins/platforms/windows/qwindowsmenu.cpp +++ b/src/plugins/platforms/windows/qwindowsmenu.cpp @@ -794,20 +794,13 @@ QWindowsMenuBar *QWindowsMenuBar::menuBarOf(const QWindow *notYetCreatedWindow) ? qobject_cast(menuBarV.value()) : nullptr; } -static inline void forceNcCalcSize(HWND hwnd) -{ - // Force WM_NCCALCSIZE to adjust margin: Does not appear to work? - SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, - SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); -} - void QWindowsMenuBar::install(QWindowsWindow *window) { const HWND hwnd = window->handle(); const BOOL result = SetMenu(hwnd, m_hMenuBar); if (result) { window->setMenuBar(this); - forceNcCalcSize(hwnd); + QWindowsContext::forceNcCalcSize(hwnd); } } @@ -817,7 +810,7 @@ void QWindowsMenuBar::removeFromWindow() const HWND hwnd = window->handle(); SetMenu(hwnd, nullptr); window->setMenuBar(nullptr); - forceNcCalcSize(hwnd); + QWindowsContext::forceNcCalcSize(hwnd); } } diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index a11da598fc..04478d5f1f 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -2431,7 +2431,17 @@ void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins) void QWindowsWindow::updateFullFrameMargins() { - // Normally obtained from WM_NCCALCSIZE + // QTBUG-82580: If a native menu is present, force a WM_NCCALCSIZE. + if (GetMenu(m_data.hwnd)) + QWindowsContext::forceNcCalcSize(m_data.hwnd); + else + calculateFullFrameMargins(); +} + +void QWindowsWindow::calculateFullFrameMargins() +{ + // Normally obtained from WM_NCCALCSIZE. This calculation only works + // when no native menu is present. const auto systemMargins = testFlag(DisableNonClientScaling) ? QWindowsGeometryHint::frameOnPrimaryScreen(m_data.hwnd) : frameMargins_sys(); diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 1f8800272b..aaf02d2a81 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -370,6 +370,7 @@ private: void handleWindowStateChange(Qt::WindowStates state); inline void destroyIcon(); void fireExpose(const QRegion ®ion, bool force=false); + void calculateFullFrameMargins(); mutable QWindowsWindowData m_data; QPointer m_menuBar; -- cgit v1.2.3 From f45d2dc54397fabca25de51fd0c9ec37014e46c8 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 10 Mar 2020 10:27:50 +0100 Subject: WinRT: Open the URLs via the XAML thread to enable them to be opened UWP expects these functions to be opened via the XAML thread, so we ensure this is done by running those functions on that thread. Change-Id: I57ae3a7d9b45d0b1a00ac23b103386bd34b65c6d Reviewed-by: Oliver Wolff --- src/plugins/platforms/winrt/qwinrtservices.cpp | 33 ++++++++++++++++---------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/plugins/platforms/winrt/qwinrtservices.cpp b/src/plugins/platforms/winrt/qwinrtservices.cpp index b27c408f40..04d7417801 100644 --- a/src/plugins/platforms/winrt/qwinrtservices.cpp +++ b/src/plugins/platforms/winrt/qwinrtservices.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -94,13 +95,17 @@ bool QWinRTServices::openUrl(const QUrl &url) HRESULT hr = d->uriFactory->CreateUri(uriString.Get(), &uri); RETURN_FALSE_IF_FAILED("Failed to create URI from QUrl."); - ComPtr> op; - hr = d->launcher->LaunchUriAsync(uri.Get(), &op); - RETURN_FALSE_IF_FAILED("Failed to start URI launch."); - boolean result; - hr = QWinRTFunctions::await(op, &result); - RETURN_FALSE_IF_FAILED("Failed to launch URI."); + hr = QEventDispatcherWinRT::runOnXamlThread([this, d, uri, &result]() { + ComPtr> op; + HRESULT hr = d->launcher->LaunchUriAsync(uri.Get(), &op); + RETURN_HR_IF_FAILED("Failed to start URI launch."); + + hr = QWinRTFunctions::await(op, &result); + RETURN_HR_IF_FAILED("Failed to launch URI."); + return hr; + }); + RETURN_FALSE_IF_FAILED("Failed to launch URI from Xaml thread."); return result; } @@ -131,12 +136,16 @@ bool QWinRTServices::openDocument(const QUrl &url) boolean result; { - ComPtr> op; - hr = d->launcher->LaunchFileAsync(file.Get(), &op); - RETURN_FALSE_IF_FAILED("Failed to start file launch."); - - hr = QWinRTFunctions::await(op, &result); - RETURN_FALSE_IF_FAILED("Failed to launch file."); + hr = QEventDispatcherWinRT::runOnXamlThread([this, d, file, &result]() { + ComPtr> op; + HRESULT hr = d->launcher->LaunchFileAsync(file.Get(), &op); + RETURN_HR_IF_FAILED("Failed to start file launch."); + + hr = QWinRTFunctions::await(op, &result); + RETURN_HR_IF_FAILED("Failed to launch file."); + return hr; + }); + RETURN_FALSE_IF_FAILED("Failed to launch file from Xaml thread."); } return result; -- cgit v1.2.3 From 97422abcfcdf2c5c5b81b9de05ceb7bef6edcf13 Mon Sep 17 00:00:00 2001 From: David Faure Date: Thu, 5 Mar 2020 17:56:32 +0100 Subject: QTreeView: don't call model.index(-1, 0) when using spanning items drawTree() does QPoint hoverPos = d->viewport->mapFromGlobal(QCursor::pos()); d->hoverBranch = d->itemDecorationAt(hoverPos); and itemDecorationAt does const QModelIndex index = q->indexAt(pos); which might very well be an invalid index. Change-Id: I7db98871543bd7e1c57fcc475d2646757bf2bb42 Reviewed-by: Christian Ehrlicher --- src/widgets/itemviews/qtreeview.cpp | 3 ++- tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp index 442369c2ec..a9e6e90623 100644 --- a/src/widgets/itemviews/qtreeview.cpp +++ b/src/widgets/itemviews/qtreeview.cpp @@ -3755,7 +3755,8 @@ int QTreeViewPrivate::itemDecorationAt(const QPoint &pos) const bool spanned = false; if (!spanningIndexes.isEmpty()) { const QModelIndex index = q->indexAt(pos); - spanned = q->isFirstColumnSpanned(index.row(), index.parent()); + if (index.isValid()) + spanned = q->isFirstColumnSpanned(index.row(), index.parent()); } const int column = spanned ? 0 : header->logicalIndexAt(pos.x()); if (!isTreePosition(column)) diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp index 0580c466cf..b26516ee6b 100644 --- a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp +++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp @@ -270,6 +270,12 @@ public: QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override { + if (onlyValidCalls) { + Q_ASSERT(row >= 0); + Q_ASSERT(column >= 0); + Q_ASSERT(row < rows); + Q_ASSERT(column < cols); + } if (row < 0 || column < 0 || (level(parent) > levels) || column >= cols || row >= rows) { return QModelIndex(); } @@ -378,6 +384,7 @@ public: mutable bool fetched = false; bool decorationsEnabled = false; bool statusTipsEnabled = false; + bool onlyValidCalls = false; }; // Testing get/set functions @@ -2420,6 +2427,7 @@ void tst_QTreeView::hiddenItems() void tst_QTreeView::spanningItems() { QtTestModel model(10, 10); + model.onlyValidCalls = true; QTreeView view; view.setModel(&model); view.show(); @@ -2459,6 +2467,8 @@ void tst_QTreeView::spanningItems() } } QCOMPARE(view.sizeHintForColumn(0), w); + + view.repaint(); // to check that this doesn't hit any assert } void tst_QTreeView::selectionOrderTest() -- cgit v1.2.3 From ffbf5ae11d8df95ec2bb0ed7c3c32be9a45eac0c Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Wed, 11 Mar 2020 19:15:10 +0100 Subject: Fix potential out-of-bounds or nullptr access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This ammends change baed8534bc1dac36a9d0ef4240fc14398076a192, which might have introduced a hard to reproduce segmentation fault when the screen number is out of bounds, or when the QScreen object doesn't return a valid pointer for QScreen::handle. As the issue doesn't reliably reproduce, this is a speculative fix that adds bounds and nullptr checking. Change-Id: I0cec0a344e80159ee1723d840f207267a608cef4 Fixes: QTBUG-82807 Reviewed-by: Tor Arne Vestbø Reviewed-by: Volker Hilsheimer --- src/widgets/kernel/qtooltip.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/widgets/kernel/qtooltip.cpp b/src/widgets/kernel/qtooltip.cpp index 1ec3612457..45835a2043 100644 --- a/src/widgets/kernel/qtooltip.cpp +++ b/src/widgets/kernel/qtooltip.cpp @@ -402,10 +402,10 @@ void QTipLabel::placeTip(const QPoint &pos, QWidget *w) #endif //QT_NO_STYLE_STYLESHEET QPoint p = pos; - int screenNumber = getTipScreen(pos, w); - QScreen *screen = QGuiApplication::screens().at(screenNumber); - if (screen) { - const QPlatformScreen *platformScreen = screen->handle(); + const QScreen *screen = QGuiApplication::screens().value(getTipScreen(pos, w), + QGuiApplication::primaryScreen()); + // a QScreen's handle *should* never be null, so this is a bit paranoid + if (const QPlatformScreen *platformScreen = screen ? screen->handle() : nullptr) { const QSize cursorSize = QHighDpi::fromNativePixels(platformScreen->cursor()->size(), platformScreen); QPoint offset(2, cursorSize.height()); -- cgit v1.2.3 From 3d25bbcdf161c49b59d6c36417a087e3c07068b6 Mon Sep 17 00:00:00 2001 From: Antti Kokko Date: Tue, 3 Mar 2020 15:10:31 +0200 Subject: Add changes file for Qt 5.14.2 Change-Id: I804e2f07ccbf077c443aef627854c6c411014269 Reviewed-by: Friedemann Kleint Reviewed-by: Alexandru Croitor Reviewed-by: Shawn Rutledge Reviewed-by: Edward Welbourne --- dist/changes-5.14.2 | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 dist/changes-5.14.2 diff --git a/dist/changes-5.14.2 b/dist/changes-5.14.2 new file mode 100644 index 0000000000..df3cabe3bf --- /dev/null +++ b/dist/changes-5.14.2 @@ -0,0 +1,117 @@ +Qt 5.14.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.14.0 through 5.14.1. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qt-5/index.html + +The Qt version 5.14 series is binary compatible with the 5.13.x series. +Applications compiled for 5.13 will continue to run with 5.14. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* QtCore * +**************************************************************************** + + - QCollator: + * QTBUG-81673: Fixed a regression introduced in 5.14.0 that caused + QCollator not to operate with default-constructed QStrings and print a + warning on Windows. + + - QString, codecs: + * QTBUG-62011: ZWNBS is no longer discarded (mistaken for a BOM despite + not being at the start) when converting UCS4 to QString's UCS2. + + - QLocale: + * QTBUG-80459: Skip digit-grouping if the system locale is configured to + use an empty group separator. + * QTBUG-81530: Use "+" if MS reports empty for the system locale's plus + sign, as documented in MS's API for the relevant query. + + - QLockFile: + * Suppressed the warning on QNX that said 'setNativeLocks failed: + "Function not implemented"'. There is no difference in behavior: Qt + will continue not to be able to apply an OS- level file lock, which + means the lock could be accidentally stolen by buggy software. Correct + software using QLockFile should not be affected. + + - QObject: + * For the purposes of QT_NO_NARROWING_CONVERSIONS_IN_CONNECT, pointer + (incl. pointer-to-member) to bool conversions are now considered + narrowing. This matches the resolution of a defect report in C++ + itself. + + - QStorageInfo: + * Improved discovery of device nodes on Linux if the /dev entry was + renamed after the filesystem was mounted and udev is in use. + +**************************************************************************** +* QtGui * +**************************************************************************** + + - QTextMarkdownImporter: + * Text in Markdown format is assumed to be UTF-8. + * The "title" in a Markdown hyperlink is now used as the tooltip, + not the anchor name. + * Fixed vulnerability oss-fuzz-20450 (invalid input resulted in an + attempt to insert items into a list that no longer exists). + +**************************************************************************** +* QtWidgets * +**************************************************************************** + + - QLineEdit: + * the inputMask property has allowed any Letter or Number category + character for the respective mask characters, not just ASCII. The + documentation has been updated accordingly. + +**************************************************************************** +* qmake * +**************************************************************************** + + - To remove the NDEBUG define that is added by default in MSVC mkspecs, + write DEFINES_RELEASE -= NDEBUG in your .pro file. + - Install/uninstall rules are now generated for target.targets on Windows. + This mirrors the behavior on Unix. + +**************************************************************************** +* Third-Party Code * +**************************************************************************** + + - md4c was updated to 0.4.3. This fixes vulnerability oss-fuzz-20580. + + - QtSQL, sqlite: + * Updated to v3.31.1 + * [QTBUG-82533] Fixed CVE-2020-9327 + +**************************************************************************** +* CMake * +**************************************************************************** + + - Windows: + * Fixed linking with Qt static build + +**************************************************************************** +* MSVC * +**************************************************************************** + + - Fixed a compatibility issue found when linking code compiled with + version 16.6 to a Qt compiled with 16.5. + +**************************************************************************** +* MinGW * +**************************************************************************** + + - Fixed build regressions. + - The -debug-and-release configuration has been fixed. In this + configuration, Qt libraries again have a 'd' suffix. + - In the -debug configuration, libraries do not have a 'd' suffix, similar + to Unix platforms. -- cgit v1.2.3 From c53cccc171c2da803cfd247e95c5dd48e5610c3b Mon Sep 17 00:00:00 2001 From: Volker Krause Date: Sat, 14 Mar 2020 13:45:10 +0100 Subject: Fix JNI signature for the timezone getDisplayName call Due to the changes in 5.14.1 this code now actually seems to be hit here, throwing NoSuchMethodError exceptions all over the place and breaking date/time handling quite spectacularly. Change-Id: I9bee3de39ec98f86d7944b94e89119505f62dc6c Reviewed-by: BogDan Vatra --- src/corelib/time/qtimezoneprivate_android.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/time/qtimezoneprivate_android.cpp b/src/corelib/time/qtimezoneprivate_android.cpp index 5cb8155dcc..fc3653752a 100644 --- a/src/corelib/time/qtimezoneprivate_android.cpp +++ b/src/corelib/time/qtimezoneprivate_android.cpp @@ -102,7 +102,7 @@ void QAndroidTimeZonePrivate::init(const QByteArray &ianaId) for (int style = 1; m_id.isEmpty() && style-- > 0;) { for (int dst = 1; m_id.isEmpty() && dst-- > 0;) { m_id = match(androidTimeZone.callObjectMethod( - "getDisplayName", "(ZI;)Ljava/lang/String;", bool(dst), style)); + "getDisplayName", "(ZI)Ljava/lang/String;", bool(dst), style)); } } } -- cgit v1.2.3 From 6db55e3451e8ba244e9f2555713d44d988977871 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Thu, 12 Mar 2020 16:25:31 +0100 Subject: QSequentialIterable: Treat sets as appendable QSet and std::(unordered_)set were so far not treated as appendable, as they lack a push_back method. We do however need support for this in declarative to enable converting back from QJSValue arrays to sets. We achieve this by testing for and using the insert method. While vector has also such a method, it doesn't take a single value, but rather a position or iterator + value, so the template specialization is not ambiguous. Task-number: QTBUG-82743 Change-Id: I74fc7b1b856d9bcd38100b274ba2b69578ea8bbb Reviewed-by: Ulf Hermann --- src/corelib/kernel/qmetatype.h | 18 +++++++++++++ .../auto/corelib/kernel/qvariant/tst_qvariant.cpp | 30 ++++++++++++++++------ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 59ec8de0e9..7c22ff1693 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -1014,6 +1014,24 @@ struct ContainerCapabilitiesImpl().p { static_cast(const_cast(container))->push_back(*static_cast(value)); } }; +namespace QtPrivate { +namespace ContainerCapabilitiesMetaProgrammingHelper { + template + using void_t = void; +} +} + +template +struct ContainerCapabilitiesImpl().insert(std::declval())), decltype(std::declval() == std::declval())>> +{ + enum {ContainerCapabilities = ContainerIsAppendable}; + + // The code below invokes undefined behavior if and only if the pointer passed into QSequentialIterableImpl + // pointed to a const object to begin with + static void appendImpl(const void *container, const void *value) + { static_cast(const_cast(container))->insert(*static_cast(value)); } +}; + template::iterator_category> struct CapabilitiesImpl; diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 70bda1a0ef..86c61cba12 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -5161,14 +5161,28 @@ void tst_QVariant::sequentialIterableEndianessSanityCheck() void tst_QVariant::sequentialIterableAppend() { - QVector container {1, 2}; - auto variant = QVariant::fromValue(container); - QVERIFY(variant.canConvert()); - auto asIterable = variant.value(); - const int i = 3, j = 4; - asIterable.append(&i); - asIterable.append(&j); - QCOMPARE(variant.value>(), QVector ({1, 2, 3, 4})); + { + QVector container {1, 2}; + auto variant = QVariant::fromValue(container); + QVERIFY(variant.canConvert()); + auto asIterable = variant.value(); + const int i = 3, j = 4; + asIterable.append(&i); + asIterable.append(&j); + QCOMPARE(variant.value>(), QVector ({1, 2, 3, 4})); + } + { + QSet container { QByteArray{"hello"}, QByteArray{"world"} }; + auto variant = QVariant::fromValue(std::move(container)); + QVERIFY(variant.canConvert()); + auto asIterable = variant.value(); + QByteArray qba1 {"goodbye"}; + QByteArray qba2 { "moon" }; + asIterable.append( &qba1 ); + asIterable.append( &qba2); + QSet reference { "hello", "world", "goodbye", "moon" }; + QCOMPARE(variant.value>(), reference); + } } void tst_QVariant::preferDirectConversionOverInterfaces() -- cgit v1.2.3 From 05dd80871c870a564a46e388d7fd124a58fc970d Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 18 Mar 2020 10:34:37 +0100 Subject: tst_QMenu: make QSKIP message truthful MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the test was refactored and QCursor::setPosition() replaced with QTest::mouseMove(), the test is completely crippled on macOS, since it relies on the parts in widget's code, ifdefed with condition !Q_OS_OSX and commented as "Cocoa tracks popups". Yes it does, but not for "fake" events generated by QTest. The original test was introduced when fixing different problems on non-Apple platform(s) anyway. Let's make QSKIP message saying the truth. Task-number: QTBUG-63031 Change-Id: If54f195ccc0d4409cc2e7f4ae0b0fbf43989b286 Reviewed-by: Tor Arne Vestbø --- tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp index 727d1c2a16..1fc1c65be0 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp @@ -1264,7 +1264,7 @@ void tst_QMenu::QTBUG47515_widgetActionEnterLeave() if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) QSKIP("Window activation is not supported"); if (QGuiApplication::platformName() == QLatin1String("cocoa")) - QSKIP("See QTBUG-63031"); + QSKIP("This test is meaningless on macOS, for additional info see QTBUG-63031"); const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); QRect geometry(QPoint(), availableGeometry.size() / 3); -- cgit v1.2.3 From fd72ed794d0325e122cd42cf3c2d0e4874b7b929 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Mon, 16 Mar 2020 15:11:44 +0100 Subject: Consider qml dependencies from qrc files in static builds qmlimportscanner already has support for qrc files, however the rule in qt.prf did not pass the required arguments to it so far. In combination with the declarative registration of types, this broke static linking. Fixes: QTBUG-82873 Change-Id: I4462645e0b353265f9953807dee73f94923dab9f Reviewed-by: Ulf Hermann Reviewed-by: Andy Shaw --- mkspecs/features/qt.prf | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mkspecs/features/qt.prf b/mkspecs/features/qt.prf index 3a71376029..fc46bcb74b 100644 --- a/mkspecs/features/qt.prf +++ b/mkspecs/features/qt.prf @@ -279,7 +279,15 @@ contains(all_qt_module_deps, qml): \ for (QMLPATH, QMLPATHS): \ IMPORTPATHS += -importPath $$system_quote($$QMLPATH) - #message(run $$QMLIMPORTSCANNER $$_PRO_FILE_PWD_ $$IMPORTPATHS) + # add qrc files, too + !isEmpty(RESOURCES) { + IMPORTPATHS += -qrcFiles + for (RESOURCE, RESOURCES): \ + IMPORTPATHS += $$absolute_path($$system_quote($$RESOURCE), $$_PRO_FILE_PWD_) + } + + + # message(run $$QMLIMPORTSCANNER $$_PRO_FILE_PWD_ $$IMPORTPATHS) JSON = $$system($$QMLIMPORTSCANNER $$system_quote($$_PRO_FILE_PWD_) $$IMPORTPATHS) parseJson(JSON, IMPORTS)| error("Failed to parse qmlimportscanner output.") -- cgit v1.2.3 From 5eb492fb9563cad4879bb77a3a5ba24e2d2109a5 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 18 Mar 2020 12:31:39 +0100 Subject: Move the default arguments of fromPath away from the deprecated overload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the default arguments to the overload that is not deprecated. This is fully source and binary compatible. Change-Id: I0d2eb491faf8c2164b80c33c4c4f749173b690f5 Reviewed-by: Mårten Nordheim --- src/network/ssl/qsslcertificate.h | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h index b98c5cfcab..147919fa3b 100644 --- a/src/network/ssl/qsslcertificate.h +++ b/src/network/ssl/qsslcertificate.h @@ -147,18 +147,13 @@ public: QString toText() const; #if QT_DEPRECATED_SINCE(5,15) - QT_DEPRECATED_X("Use the overload not using QRegExp") static QList fromPath( - const QString &path, QSsl::EncodingFormat format = QSsl::Pem, - QRegExp::PatternSyntax syntax = QRegExp::FixedString); - - static QList fromPath( - const QString &path, QSsl::EncodingFormat format, - PatternSyntax syntax); -#else - static QList fromPath( - const QString &path, QSsl::EncodingFormat format = QSsl::Pem, - PatternSyntax syntax = FixedString); + QT_DEPRECATED_X("Use the overload not using QRegExp") + static QList fromPath(const QString &path, QSsl::EncodingFormat format, + QRegExp::PatternSyntax syntax); #endif + static QList fromPath(const QString &path, + QSsl::EncodingFormat format = QSsl::Pem, + PatternSyntax syntax = PatternSyntax::FixedString); static QList fromDevice( QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem); -- cgit v1.2.3 From 1a18e138f4d17c219c6ffd6e4396c30b960818b4 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Wed, 18 Mar 2020 13:57:21 +0100 Subject: Android: Fix deployment on Windows host The paths in the build properties require forward slashes apparently. On Windows, we would default to native backslashes and they would be stripped from the path. Converting to forward slashes fixes the problem. Issue was introduced by dd04fb639bf357e66d0586faed78a3043a62819e, since before that, the NDK path was retrieved from the environment. Fixes: QTBUG-82944 Change-Id: I6c51113efcf671461a5871991b3225a52b95266c Reviewed-by: BogDan Vatra --- src/tools/androiddeployqt/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index 80612d34ac..c2710c5619 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -2287,8 +2287,8 @@ static bool mergeGradleProperties(const QString &path, GradleProperties properti bool buildAndroidProject(const Options &options) { GradleProperties localProperties; - localProperties["sdk.dir"] = options.sdkPath.toUtf8(); - localProperties["ndk.dir"] = options.ndkPath.toUtf8(); + localProperties["sdk.dir"] = QDir::fromNativeSeparators(options.sdkPath).toUtf8(); + localProperties["ndk.dir"] = QDir::fromNativeSeparators(options.ndkPath).toUtf8(); if (!mergeGradleProperties(options.outputDirectory + QLatin1String("local.properties"), localProperties)) return false; -- cgit v1.2.3 From ad68ecf1d967f8e60c19c28a2bc23daf15389076 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 18 Mar 2020 19:54:33 +0100 Subject: q_getTimeFromASN1: fix invalid access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No sanitizer is needed, just looking at the code is enough. It was wrong. Change-Id: I9df417c137d6b3361c3161865e099a8be40860de Reviewed-by: Lars Knoll Reviewed-by: Mårten Nordheim --- src/network/ssl/qsslsocket_openssl_symbols.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 26336edd3d..85029a6ff3 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -1428,6 +1428,9 @@ QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime) { size_t lTimeLength = aTime->length; char *pString = (char *) aTime->data; + auto isValidPointer = [pString, lTimeLength](const char *const probe){ + return size_t(probe - pString) < lTimeLength; + }; if (aTime->type == V_ASN1_UTCTIME) { @@ -1446,12 +1449,21 @@ QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime) *pBuffer++ = '0'; } else { *pBuffer++ = *pString++; + if (!isValidPointer(pString)) // Nah. + return {}; *pBuffer++ = *pString++; + if (!isValidPointer(pString)) // Nah. + return {}; // Skip any fractional seconds... if (*pString == '.') { pString++; - while ((*pString >= '0') && (*pString <= '9')) + if (!isValidPointer(pString)) // Oh no, cannot dereference (see below). + return {}; + while ((*pString >= '0') && (*pString <= '9')) { pString++; + if (!isValidPointer(pString)) // No and no. + return {}; + } } } @@ -1465,6 +1477,10 @@ QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime) if ((*pString != '+') && (*pString != '-')) return QDateTime(); + if (!isValidPointer(pString + 4)) { + // What kind of input parameters we were provided with? To hell with them! + return {}; + } lSecondsFromUCT = ((pString[1] - '0') * 10 + (pString[2] - '0')) * 60; lSecondsFromUCT += (pString[3] - '0') * 10 + (pString[4] - '0'); lSecondsFromUCT *= 60; -- cgit v1.2.3 From cfbe4818385b22128755e13cd8ab3c75182853c2 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 18 Mar 2020 12:23:22 +0100 Subject: QDrawHelper cleanups We don't need to handle solid SourceOver logic directly, this was already handled by getOperator and changed composition to Source. Also removes some dead code and changes an assert in unreachable code to Q_UNREACHABLE. Change-Id: I66a6c1248bd34e31096023f1acb20385099932c9 Reviewed-by: Eirik Aavitsland --- src/gui/painting/qdrawhelper.cpp | 10 ++++------ src/gui/painting/qdrawhelper_p.h | 2 +- src/gui/painting/qdrawhelper_x86_p.h | 3 --- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 2d4045fe29..4b0cc2547c 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -4482,9 +4482,8 @@ static void blend_color_generic(int count, const QSpan *spans, void *userData) uint buffer[BufferSize]; Operator op = getOperator(data, nullptr, 0); const uint color = data->solidColor.toArgb32(); - bool solidFill = data->rasterBuffer->compositionMode == QPainter::CompositionMode_Source - || (data->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver && qAlpha(color) == 255); - QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp; + const bool solidFill = op.mode == QPainter::CompositionMode_Source; + const QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp; while (count--) { int x = spans->x; @@ -4552,9 +4551,8 @@ void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData) alignas(8) QRgba64 buffer[BufferSize]; const QRgba64 color = data->solidColor; - bool solidFill = data->rasterBuffer->compositionMode == QPainter::CompositionMode_Source - || (data->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver && color.isOpaque()); - QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp; + const bool solidFill = op.mode == QPainter::CompositionMode_Source; + const QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp; while (count--) { int x = spans->x; diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index dd42b96d79..f1ad369906 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -601,7 +601,7 @@ public: FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP_PAD) break; default: - Q_ASSERT(false); + Q_UNREACHABLE(); } } }; diff --git a/src/gui/painting/qdrawhelper_x86_p.h b/src/gui/painting/qdrawhelper_x86_p.h index 5749d8c9fb..869abcc637 100644 --- a/src/gui/painting/qdrawhelper_x86_p.h +++ b/src/gui/painting/qdrawhelper_x86_p.h @@ -77,9 +77,6 @@ void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, int w, int h, int const_alpha); -extern CompositionFunction qt_functionForMode_SSE2[]; -extern CompositionFunctionSolid qt_functionForModeSolid_SSE2[]; - void qt_memfill64_avx2(quint64 *dest, quint64 value, qsizetype count); void qt_memfill32_avx2(quint32 *dest, quint32 value, qsizetype count); #endif // __SSE2__ -- cgit v1.2.3 From 5f3c071ee3dc5536227055573af5a17d3aaa1a11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 17 Mar 2020 14:49:06 +0100 Subject: widgets: Remove unused member QTLWExtra::inTopLevelResize It was introduced in Qt 4.4 (e150f6a6e619) to work around slow resizes on Windows and X11 due to excessive painting, but has since been removed when old dead code never ported to QPA was removed in a2337f79ffd229. Change-Id: Ic14e714a02edb4194a445a6bb0759b601799fdc6 Reviewed-by: Paul Olav Tvete --- src/widgets/kernel/qwidget.cpp | 5 ++--- src/widgets/kernel/qwidget_p.h | 1 - src/widgets/kernel/qwidgetrepaintmanager.cpp | 15 ++++----------- src/widgets/kernel/qwidgetwindow.cpp | 2 +- 4 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 691a169269..fe57ac3a7e 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1592,7 +1592,6 @@ void QWidgetPrivate::createTLExtra() x->opacity = 255; x->posIncludesFrame = 0; x->sizeAdjusted = false; - x->inTopLevelResize = false; x->embedded = 0; x->window = nullptr; x->initialScreenIndex = -1; @@ -10790,7 +10789,7 @@ void QWidgetPrivate::repaint(T r) return; QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); - if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) + if (tlwExtra && tlwExtra->backingStore) tlwExtra->repaintManager->markDirty(r, q, QWidgetRepaintManager::UpdateNow); } @@ -10865,7 +10864,7 @@ void QWidgetPrivate::update(T r) } QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); - if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) + if (tlwExtra && tlwExtra->backingStore) tlwExtra->repaintManager->markDirty(clipped, q); } diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index 2597017318..6aadfe13f7 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -155,7 +155,6 @@ struct QTLWExtra { uint opacity : 8; uint posIncludesFrame : 1; uint sizeAdjusted : 1; - uint inTopLevelResize : 1; uint embedded : 1; }; diff --git a/src/widgets/kernel/qwidgetrepaintmanager.cpp b/src/widgets/kernel/qwidgetrepaintmanager.cpp index 135a1527ac..523e226c08 100644 --- a/src/widgets/kernel/qwidgetrepaintmanager.cpp +++ b/src/widgets/kernel/qwidgetrepaintmanager.cpp @@ -166,7 +166,7 @@ void QWidgetPrivate::invalidateBackingStore(const T &r) return; QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); - if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore) + if (!tlwExtra || !tlwExtra->backingStore) return; T clipped(r); @@ -213,7 +213,6 @@ void QWidgetRepaintManager::markDirty(const T &r, QWidget *widget, UpdateTime up Q_ASSERT(tlw->d_func()->extra); Q_ASSERT(tlw->d_func()->extra->topextra); - Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize); Q_ASSERT(widget->isVisible() && widget->updatesEnabled()); Q_ASSERT(widget->window() == tlw); Q_ASSERT(!r.isEmpty()); @@ -446,8 +445,6 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy) QWidget *tlw = q->window(); QTLWExtra* x = tlw->d_func()->topData(); - if (x->inTopLevelResize) - return; static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_MOVE") == 0; @@ -543,8 +540,6 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy) Q_Q(QWidget); QWidget *tlw = q->window(); QTLWExtra* x = tlw->d_func()->topData(); - if (x->inTopLevelResize) - return; QWidgetRepaintManager *repaintManager = x->repaintManager.get(); if (!repaintManager) @@ -722,8 +717,7 @@ void QWidgetRepaintManager::sync(QWidget *exposedWidget, const QRegion &exposedR { qCInfo(lcWidgetPainting) << "Syncing" << exposedRegion << "of" << exposedWidget; - QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); - if (!tlw->isVisible() || !tlwExtra || tlwExtra->inTopLevelResize) + if (!tlw->isVisible()) return; if (!exposedWidget || !hasPlatformWindow(exposedWidget) @@ -815,10 +809,9 @@ void QWidgetRepaintManager::paintAndFlush() const bool updatesDisabled = !tlw->updatesEnabled(); bool repaintAllWidgets = false; - const bool inTopLevelResize = tlw->d_func()->maybeTopData()->inTopLevelResize; const QRect tlwRect = tlw->data->crect; const QRect surfaceGeometry(tlwRect.topLeft(), store->size()); - if ((inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) { + if ((surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) { if (hasStaticContents() && !store->size().isEmpty() ) { // Repaint existing dirty area and newly visible area. const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); @@ -837,7 +830,7 @@ void QWidgetRepaintManager::paintAndFlush() } } - if (inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) + if (surfaceGeometry.size() != tlwRect.size()) store->resize(tlwRect.size()); if (updatesDisabled) diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index e82ddbcd20..67b7d05499 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -764,7 +764,7 @@ void QWidgetWindow::repaintWindow() return; QTLWExtra *tlwExtra = m_widget->window()->d_func()->maybeTopData(); - if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) + if (tlwExtra && tlwExtra->backingStore) tlwExtra->repaintManager->markDirty(m_widget->rect(), m_widget, QWidgetRepaintManager::UpdateNow, QWidgetRepaintManager::BufferInvalid); } -- cgit v1.2.3 From 45fe7adcb89609e8dc909ba3e55ab03abe7e4722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 17 Mar 2020 15:14:57 +0100 Subject: widgets: Clarify backingstore resize logic by removing cruft Change-Id: I0a449068a0d4557b7bd6581ddf71c590b72d76a1 Reviewed-by: Paul Olav Tvete --- src/widgets/kernel/qwidgetrepaintmanager.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/widgets/kernel/qwidgetrepaintmanager.cpp b/src/widgets/kernel/qwidgetrepaintmanager.cpp index 523e226c08..e7e85c39e7 100644 --- a/src/widgets/kernel/qwidgetrepaintmanager.cpp +++ b/src/widgets/kernel/qwidgetrepaintmanager.cpp @@ -810,11 +810,10 @@ void QWidgetRepaintManager::paintAndFlush() bool repaintAllWidgets = false; const QRect tlwRect = tlw->data->crect; - const QRect surfaceGeometry(tlwRect.topLeft(), store->size()); - if ((surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) { + if (!updatesDisabled && store->size() != tlwRect.size()) { if (hasStaticContents() && !store->size().isEmpty() ) { // Repaint existing dirty area and newly visible area. - const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); + const QRect clipRect(QPoint(0, 0), store->size()); const QRegion staticRegion(staticContents(nullptr, clipRect)); QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height()); newVisible -= staticRegion; @@ -830,7 +829,7 @@ void QWidgetRepaintManager::paintAndFlush() } } - if (surfaceGeometry.size() != tlwRect.size()) + if (store->size() != tlwRect.size()) store->resize(tlwRect.size()); if (updatesDisabled) @@ -1241,11 +1240,10 @@ bool QWidgetRepaintManager::hasStaticContents() const QRegion QWidgetRepaintManager::staticContents(QWidget *parent, const QRect &withinClipRect) const { if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) { - const QSize surfaceGeometry(store->size()); - QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); + QRect backingstoreRect(QPoint(0, 0), store->size()); if (!withinClipRect.isEmpty()) - surfaceRect &= withinClipRect; - return QRegion(surfaceRect); + backingstoreRect &= withinClipRect; + return QRegion(backingstoreRect); } QRegion region; -- cgit v1.2.3 From b77e239c5e7d3befbd48200aa51c5ff9b51d07f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Wed, 18 Mar 2020 09:39:36 +0100 Subject: Fix perl script warning The apache logs are filled with warnings about this when it's used. https://blog.gerv.net/2014/10/new-class-of-vulnerability-in-perl-web-applications/ Change-Id: I977d2b022d706d9587c033fd8e80f129e60c439c Reviewed-by: Timur Pocheptsov Reviewed-by: Edward Welbourne --- tests/testserver/apache2/testdata/www/cgi-bin/multipart.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testserver/apache2/testdata/www/cgi-bin/multipart.cgi b/tests/testserver/apache2/testdata/www/cgi-bin/multipart.cgi index 6973875cc9..60c0c0e8ad 100755 --- a/tests/testserver/apache2/testdata/www/cgi-bin/multipart.cgi +++ b/tests/testserver/apache2/testdata/www/cgi-bin/multipart.cgi @@ -11,7 +11,7 @@ print "content type: $contentType\n"; if ($contentType =~ /^multipart\/form-data/) { foreach my $key ($q->param) { - foreach my $value ($q->param($key)) { + foreach my $value (scalar $q->param($key)) { if ($key =~ /text/) { $retValue = $value; } else { -- cgit v1.2.3 From 0359a82e6ef538316e550e7fa7c6dee8db72a225 Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Tue, 28 Jan 2020 12:41:16 +0100 Subject: Doc: make Qt Sql snippets compilable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-81496 Change-Id: Id6206e9179c2e8157c99e777a3de35bd83d49e34 Reviewed-by: Topi Reiniö Reviewed-by: Paul Wicking --- src/sql/doc/snippets/code/doc_src_sql-driver.cpp | 61 ++-- .../snippets/code/doc_src_sql-driver_snippet.cpp | 66 ++++ .../snippets/code/src_sql_kernel_qsqldatabase.cpp | 73 ++-- .../code/src_sql_kernel_qsqldatabase_snippet.cpp | 69 ++++ .../snippets/code/src_sql_kernel_qsqldriver.cpp | 26 +- .../doc/snippets/code/src_sql_kernel_qsqlerror.cpp | 8 + .../doc/snippets/code/src_sql_kernel_qsqlquery.cpp | 22 +- .../code/src_sql_kernel_qsqlquery_snippet.cpp | 58 +++ .../snippets/code/src_sql_kernel_qsqlresult.cpp | 33 +- .../code/src_sql_kernel_qsqlresult_snippet.cpp | 65 ++++ .../code/src_sql_models_qsqlquerymodel.cpp | 10 +- src/sql/doc/snippets/snippets.pro | 13 + src/sql/doc/snippets/sqldatabase/sqldatabase.cpp | 48 +-- .../snippets/sqldatabase/sqldatabase_snippet.cpp | 387 +++++++++++++++++++++ src/sql/doc/src/sql-driver.qdoc | 2 +- src/sql/doc/src/sql-programming.qdoc | 8 +- src/sql/kernel/qsqldatabase.cpp | 8 +- src/sql/kernel/qsqlquery.cpp | 4 +- src/sql/kernel/qsqlresult.cpp | 2 +- src/sql/models/qsqlquerymodel.cpp | 2 +- src/sql/models/qsqltablemodel.cpp | 2 +- src/src.pro | 7 + 22 files changed, 811 insertions(+), 163 deletions(-) create mode 100644 src/sql/doc/snippets/code/doc_src_sql-driver_snippet.cpp create mode 100644 src/sql/doc/snippets/code/src_sql_kernel_qsqldatabase_snippet.cpp create mode 100644 src/sql/doc/snippets/code/src_sql_kernel_qsqlquery_snippet.cpp create mode 100644 src/sql/doc/snippets/code/src_sql_kernel_qsqlresult_snippet.cpp create mode 100644 src/sql/doc/snippets/snippets.pro create mode 100644 src/sql/doc/snippets/sqldatabase/sqldatabase_snippet.cpp diff --git a/src/sql/doc/snippets/code/doc_src_sql-driver.cpp b/src/sql/doc/snippets/code/doc_src_sql-driver.cpp index 54576733bf..076a3367dc 100644 --- a/src/sql/doc/snippets/code/doc_src_sql-driver.cpp +++ b/src/sql/doc/snippets/code/doc_src_sql-driver.cpp @@ -47,7 +47,15 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - +#include +#include +#include +#include +#include +#include + +void testProc() +{ //! [2] QSqlQuery q; q.exec("call qtestproc (@outval1, @outval2)"); @@ -55,17 +63,22 @@ q.exec("select @outval1, @outval2"); q.next(); qDebug() << q.value(0) << q.value(1); // outputs "42" and "43" //! [2] +} - +void callStoredProc() +{ //! [10] // STORED_PROC uses the return statement or returns multiple result sets QSqlQuery query; query.setForwardOnly(true); query.exec("{call STORED_PROC}"); //! [10] +} - +void setHost() +{ //! [24] +QSqlDatabase db; db.setHostName("MyServer"); db.setDatabaseName("C:\\test.gdb"); //! [24] @@ -76,8 +89,10 @@ db.setDatabaseName("C:\\test.gdb"); db.setConnectOptions("ISC_DPB_LC_CTYPE=Latin1"); db.open(); //! [25] +} - +void exProc() +{ //! [26] QSqlQuery q; q.exec("execute procedure my_procedure"); @@ -85,50 +100,43 @@ q.next(); qDebug() << q.value(0); // outputs the first RETURN/OUT value //! [26] - +qDebug( \ //! [31] -QSqlDatabase: QMYSQL driver not loaded -QSqlDatabase: available drivers: QMYSQL +"QSqlDatabase: QMYSQL driver not loaded \ +QSqlDatabase: available drivers: QMYSQL" \ //! [31] +); - +/* Commented because the following line is not compilable //! [34] column.contains(QRegularExpression("pattern")); //! [34] - - -//! [36] -QSqlQuery query(db); -query.setForwardOnly(true); -query.exec("SELECT * FROM table"); -while (query.next()) { - // Handle changes in every iteration of the loop - QVariant v = query.result()->handle(); - if (qstrcmp(v.typeName(), "PGresult*") == 0) { - PGresult *handle = *static_cast(v.data()); - if (handle) { - // Do something... - } - } +*/ } -//! [36] + +void updTable2() +{ +QSqlDatabase db; //! [37] int value; -QSqlQuery query1(db); +QSqlQuery query1; query1.setForwardOnly(true); query1.exec("select * FROM table1"); while (query1.next()) { value = query1.value(0).toInt(); if (value == 1) { - QSqlQuery query2(db); + QSqlQuery query2; query2.exec("update table2 set col=2"); // WRONG: This will discard all results of } // query1, and cause the loop to quit } //! [37] +} +void setConnString() +{ //! [39] QSqlDatabase db = QSqlDatabase::addDatabase("QODBC3"); QString connectString = QStringLiteral( @@ -139,3 +147,4 @@ QString connectString = QStringLiteral( "SCROLLABLERESULT=true"); db.setDatabaseName(connectString); //! [39] +} diff --git a/src/sql/doc/snippets/code/doc_src_sql-driver_snippet.cpp b/src/sql/doc/snippets/code/doc_src_sql-driver_snippet.cpp new file mode 100644 index 0000000000..7cffe58ff1 --- /dev/null +++ b/src/sql/doc/snippets/code/doc_src_sql-driver_snippet.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +//! [36] +QSqlQuery query; +QVariant v; +query.setForwardOnly(true); +query.exec("SELECT * FROM table"); +while (query.next()) { + // Handle changes in every iteration of the loop + v = query.result()->handle(); + + if (qstrcmp(v.typeName(), "PGresult*") == 0) { + PGresult *handle = *static_cast(v.data()); + if (handle) { + // Do something... + } + } +} +//! [36] diff --git a/src/sql/doc/snippets/code/src_sql_kernel_qsqldatabase.cpp b/src/sql/doc/snippets/code/src_sql_kernel_qsqldatabase.cpp index f09315435e..c7ceb847da 100644 --- a/src/sql/doc/snippets/code/src_sql_kernel_qsqldatabase.cpp +++ b/src/sql/doc/snippets/code/src_sql_kernel_qsqldatabase.cpp @@ -47,18 +47,25 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ +#include +#include +#include +#include +void openDatabase() +{ //! [0] // WRONG QSqlDatabase db = QSqlDatabase::database("sales"); QSqlQuery query("SELECT NAME, DOB FROM EMPLOYEES", db); QSqlDatabase::removeDatabase("sales"); // will output a warning - // "db" is now a dangling invalid database connection, // "query" contains an invalid result set //! [0] +} - +void removeDatabase() +{ //! [1] { QSqlDatabase db = QSqlDatabase::database("sales"); @@ -67,72 +74,51 @@ QSqlDatabase::removeDatabase("sales"); // will output a warning // Both "db" and "query" are destroyed because they are out of scope QSqlDatabase::removeDatabase("sales"); // correct //! [1] +} - -//! [2] -QSqlDatabase::registerSqlDriver("MYDRIVER", - new QSqlDriverCreator); -QSqlDatabase db = QSqlDatabase::addDatabase("MYDRIVER"); -//! [2] - - +void setmyDatabase() +{ //! [3] -... -db = QSqlDatabase::addDatabase("QODBC"); +// ... +QSqlDatabase db = QSqlDatabase::addDatabase("QODBC"); db.setDatabaseName("DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};FIL={MS Access};DBQ=myaccessfile.mdb"); if (db.open()) { // success! } -... +// ... //! [3] +} - -//! [4] -... +// ... // MySQL connection +void dbConnect() +{ +QSqlDatabase db; +//! [4] db.setConnectOptions("SSL_KEY=client-key.pem;SSL_CERT=client-cert.pem;SSL_CA=ca-cert.pem;CLIENT_IGNORE_SPACE=1"); // use an SSL connection to the server if (!db.open()) { db.setConnectOptions(); // clears the connect option string - ... + // ... } -... +// ... // PostgreSQL connection db.setConnectOptions("requiressl=1"); // enable PostgreSQL SSL connections if (!db.open()) { db.setConnectOptions(); // clear options - ... + // ... } -... +// ... // ODBC connection db.setConnectOptions("SQL_ATTR_ACCESS_MODE=SQL_MODE_READ_ONLY;SQL_ATTR_TRACE=SQL_OPT_TRACE_ON"); // set ODBC options if (!db.open()) { db.setConnectOptions(); // don't try to set this option - ... + // ... +} } //! [4] - -//! [5] -#include "qtdir/src/sql/drivers/psql/qsql_psql.cpp" -//! [5] - - -//! [6] -PGconn *con = PQconnectdb("host=server user=bart password=simpson dbname=springfield"); -QPSQLDriver *drv = new QPSQLDriver(con); -QSqlDatabase db = QSqlDatabase::addDatabase(drv); // becomes the new default connection -QSqlQuery query; -query.exec("SELECT NAME, ID FROM STAFF"); -... -//! [6] - - -//! [7] -unix:LIBS += -lpq -win32:LIBS += libpqdll.lib -//! [7] - - +void dbQdebug() +{ //! [8] QSqlDatabase db; qDebug() << db.isValid(); // Returns false @@ -143,3 +129,4 @@ qDebug() << db.isValid(); // Returns \c true if "sales" connection exists QSqlDatabase::removeDatabase("sales"); qDebug() << db.isValid(); // Returns false //! [8] +} diff --git a/src/sql/doc/snippets/code/src_sql_kernel_qsqldatabase_snippet.cpp b/src/sql/doc/snippets/code/src_sql_kernel_qsqldatabase_snippet.cpp new file mode 100644 index 0000000000..a53880fee1 --- /dev/null +++ b/src/sql/doc/snippets/code/src_sql_kernel_qsqldatabase_snippet.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [2] +QSqlDatabase::registerSqlDriver("MYDRIVER", new QSqlDriverCreator); +QVERIFY(QSqlDatabase::drivers().contains("MYDRIVER")); +QSqlDatabase db = QSqlDatabase::addDatabase("MYDRIVER"); +QVERIFY(db.isValid()); +//! [2] +//! [6] +PGconn *con = PQconnectdb("host=server user=bart password=simpson dbname=springfield"); +QPSQLDriver *drv = new QPSQLDriver(con); +QSqlDatabase db = QSqlDatabase::addDatabase(drv); // becomes the new default connection +QSqlQuery query; +query.exec("SELECT NAME, ID FROM STAFF"); +//! [6] + +//! [7] +unix:LIBS += -lpq +win32:LIBS += libpqdll.lib +//! [7] + diff --git a/src/sql/doc/snippets/code/src_sql_kernel_qsqldriver.cpp b/src/sql/doc/snippets/code/src_sql_kernel_qsqldriver.cpp index a13cf86d3f..47e8701149 100644 --- a/src/sql/doc/snippets/code/src_sql_kernel_qsqldriver.cpp +++ b/src/sql/doc/snippets/code/src_sql_kernel_qsqldriver.cpp @@ -47,28 +47,42 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ +#include +#include +#include +#include +void checkHandle() +{ +//dummy definitions +typedef void sqlite3; +typedef void PGconn; +typedef void MYSQL; //! [0] -QSqlDatabase db = ...; +QSqlDatabase db = QSqlDatabase::database(); QVariant v = db.driver()->handle(); -if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*") == 0) { +if (v.isValid() && (qstrcmp(v.typeName(), "sqlite3*") == 0)) { // v.data() returns a pointer to the handle sqlite3 *handle = *static_cast(v.data()); if (handle) { - ... + // ... } } //! [0] - //! [1] if (qstrcmp(v.typeName(), "PGconn*") == 0) { PGconn *handle = *static_cast(v.data()); - if (handle) ... + if (handle) { + // ... + } } if (qstrcmp(v.typeName(), "MYSQL*") == 0) { MYSQL *handle = *static_cast(v.data()); - if (handle) ... + if (handle) { + // ... + } } //! [1] +} diff --git a/src/sql/doc/snippets/code/src_sql_kernel_qsqlerror.cpp b/src/sql/doc/snippets/code/src_sql_kernel_qsqlerror.cpp index 9f6c5da57c..d442768fe2 100644 --- a/src/sql/doc/snippets/code/src_sql_kernel_qsqlerror.cpp +++ b/src/sql/doc/snippets/code/src_sql_kernel_qsqlerror.cpp @@ -47,10 +47,18 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ +#include +#include +#include +#include +#include +void checkSqlQueryModel() +{ //! [0] QSqlQueryModel model; model.setQuery("select * from myTable"); if (model.lastError().isValid()) qDebug() << model.lastError(); //! [0] +} diff --git a/src/sql/doc/snippets/code/src_sql_kernel_qsqlquery.cpp b/src/sql/doc/snippets/code/src_sql_kernel_qsqlquery.cpp index b99745c749..496c971621 100644 --- a/src/sql/doc/snippets/code/src_sql_kernel_qsqlquery.cpp +++ b/src/sql/doc/snippets/code/src_sql_kernel_qsqlquery.cpp @@ -47,12 +47,13 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ +#include +#include +#include +#include -//! [0] -SELECT forename, surname FROM people; -//! [0] - - +void selectEmployees() +{ //! [1] QSqlQuery q("select * from employees"); QSqlRecord rec = q.record(); @@ -63,8 +64,6 @@ int nameCol = rec.indexOf("name"); // index of the field "name" while (q.next()) qDebug() << q.value(nameCol).toString(); // output all names //! [1] - - //! [2] QSqlQuery q; q.prepare("insert into myTable values (?, ?)"); @@ -80,11 +79,4 @@ q.addBindValue(names); if (!q.execBatch()) qDebug() << q.lastError(); //! [2] - - -//! [3] -1 Harald -2 Boris -3 Trond -4 NULL -//! [3] +} diff --git a/src/sql/doc/snippets/code/src_sql_kernel_qsqlquery_snippet.cpp b/src/sql/doc/snippets/code/src_sql_kernel_qsqlquery_snippet.cpp new file mode 100644 index 0000000000..d7d2a14d56 --- /dev/null +++ b/src/sql/doc/snippets/code/src_sql_kernel_qsqlquery_snippet.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +//! [0] +SELECT forename, surname FROM people; +//! [0] +//! [3] +1 Harald +2 Boris +3 Trond +4 NULL +//! [3] diff --git a/src/sql/doc/snippets/code/src_sql_kernel_qsqlresult.cpp b/src/sql/doc/snippets/code/src_sql_kernel_qsqlresult.cpp index 8ab2baf2a1..606b6d19fa 100644 --- a/src/sql/doc/snippets/code/src_sql_kernel_qsqlresult.cpp +++ b/src/sql/doc/snippets/code/src_sql_kernel_qsqlresult.cpp @@ -47,7 +47,18 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ +#include +#include +#include +#include +#include +#include +// dummy typedef +typedef void *sqlite3_stmt; + +void insertVariants() +{ //! [0] QSqlQuery q; q.prepare("insert into test (i1, i2, s) values (?, ?, ?)"); @@ -67,29 +78,21 @@ q.bindValue(2, col3); if (!q.execBatch()) qDebug() << q.lastError(); //! [0] +} - +void querySqlite() +{ //! [1] -QSqlQuery query = ... +QSqlDatabase db = QSqlDatabase::database("sales"); +QSqlQuery query("SELECT NAME, DOB FROM EMPLOYEES", db); + QVariant v = query.result()->handle(); if (v.isValid() && qstrcmp(v.typeName(), "sqlite3_stmt*") == 0) { // v.data() returns a pointer to the handle sqlite3_stmt *handle = *static_cast(v.data()); if (handle) { - ... + // ... } } //! [1] - - -//! [2] -if (qstrcmp(v.typeName(), "PGresult*") == 0) { - PGresult *handle = *static_cast(v.data()); - if (handle) ... -} - -if (qstrcmp(v.typeName(), "MYSQL_STMT*") == 0) { - MYSQL_STMT *handle = *static_cast(v.data()); - if (handle) ... } -//! [2] diff --git a/src/sql/doc/snippets/code/src_sql_kernel_qsqlresult_snippet.cpp b/src/sql/doc/snippets/code/src_sql_kernel_qsqlresult_snippet.cpp new file mode 100644 index 0000000000..2b1891f6c1 --- /dev/null +++ b/src/sql/doc/snippets/code/src_sql_kernel_qsqlresult_snippet.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [2] +if (qstrcmp(v.typeName(), "PGresult*") == 0) { + PGresult *handle = *static_cast(v.data()); + if (handle) { + // ... + } +} + +if (qstrcmp(v.typeName(), "MYSQL_STMT*") == 0) { + MYSQL_STMT *handle = *static_cast(v.data()); + if (handle) { + // ... + } + } +//! [2] diff --git a/src/sql/doc/snippets/code/src_sql_models_qsqlquerymodel.cpp b/src/sql/doc/snippets/code/src_sql_models_qsqlquerymodel.cpp index b3a43537a1..cb2bde6c7d 100644 --- a/src/sql/doc/snippets/code/src_sql_models_qsqlquerymodel.cpp +++ b/src/sql/doc/snippets/code/src_sql_models_qsqlquerymodel.cpp @@ -47,16 +47,24 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ +#include +#include +#include +#include +#include "../sqldatabase/sqldatabase.cpp" +void MyModel::fetchModel() +{ +MyModel *myModel = new MyModel; //! [0] while (myModel->canFetchMore()) myModel->fetchMore(); //! [0] - //! [1] QSqlQueryModel model; model.setQuery("select * from MyTable"); if (model.lastError().isValid()) qDebug() << model.lastError(); //! [1] +} diff --git a/src/sql/doc/snippets/snippets.pro b/src/sql/doc/snippets/snippets.pro new file mode 100644 index 0000000000..46eabbb0a1 --- /dev/null +++ b/src/sql/doc/snippets/snippets.pro @@ -0,0 +1,13 @@ +TEMPLATE = app +TARGET = sqldatabase_cppsnippet +QT = core sql sql-private + +SOURCES += sqldatabase/sqldatabase.cpp \ + code/doc_src_qtsql.cpp \ + code/doc_src_sql-driver.cpp \ + code/src_sql_kernel_qsqldatabase.cpp \ + code/src_sql_kernel_qsqlerror.cpp \ + code/src_sql_kernel_qsqlresult.cpp \ + code/src_sql_kernel_qsqldriver.cpp \ + code/src_sql_models_qsqlquerymodel.cpp + diff --git a/src/sql/doc/snippets/sqldatabase/sqldatabase.cpp b/src/sql/doc/snippets/sqldatabase/sqldatabase.cpp index a45b5f409a..2039007c4a 100644 --- a/src/sql/doc/snippets/sqldatabase/sqldatabase.cpp +++ b/src/sql/doc/snippets/sqldatabase/sqldatabase.cpp @@ -48,18 +48,13 @@ ** ****************************************************************************/ -#include +#include #include - +#include #include using namespace std; -QString tr(const char *text) -{ - return QApplication::translate(text, text); -} - void QSqlDatabase_snippets() { { @@ -209,9 +204,8 @@ void QSqlQuery_snippets() { // examine with named binding //! [14] - QMapIterator i(query.boundValues()); - while (i.hasNext()) { - i.next(); + QMap sqlIterator(query.boundValues()); + for (auto i = sqlIterator.begin(); i != sqlIterator.end(); ++i) { cout << i.key().toUtf8().data() << ": " << i.value().toString().toUtf8().data() << Qt::endl; } @@ -230,23 +224,6 @@ void QSqlQuery_snippets() void QSqlQueryModel_snippets() { - { -//! [16] - QSqlQueryModel *model = new QSqlQueryModel; - model->setQuery("SELECT name, salary FROM employee"); - model->setHeaderData(0, Qt::Horizontal, tr("Name")); - model->setHeaderData(1, Qt::Horizontal, tr("Salary")); - -//! [17] - QTableView *view = new QTableView; -//! [17] //! [18] - view->setModel(model); -//! [18] //! [19] - view->show(); -//! [16] //! [19] //! [20] - view->setEditTriggers(QAbstractItemView::NoEditTriggers); -//! [20] - } //! [21] QSqlQueryModel model; @@ -273,6 +250,7 @@ class MyModel : public QSqlQueryModel { public: QVariant data(const QModelIndex &item, int role) const override; + void fetchModel(); int m_specialColumnNo; }; @@ -289,20 +267,6 @@ QVariant MyModel::data(const QModelIndex &item, int role) const void QSqlTableModel_snippets() { -//! [24] - QSqlTableModel *model = new QSqlTableModel(parentObject, database); - model->setTable("employee"); - model->setEditStrategy(QSqlTableModel::OnManualSubmit); - model->select(); - model->setHeaderData(0, Qt::Horizontal, tr("Name")); - model->setHeaderData(1, Qt::Horizontal, tr("Salary")); - - QTableView *view = new QTableView; - view->setModel(model); - view->hideColumn(0); // don't show the ID - view->show(); -//! [24] - { //! [25] QSqlTableModel model; @@ -557,7 +521,7 @@ public: int main(int argc, char **argv) { - QApplication app(argc, argv); + QCoreApplication app(argc, argv); QSqlDatabase_snippets(); QSqlField_snippets(); diff --git a/src/sql/doc/snippets/sqldatabase/sqldatabase_snippet.cpp b/src/sql/doc/snippets/sqldatabase/sqldatabase_snippet.cpp new file mode 100644 index 0000000000..604eb97e58 --- /dev/null +++ b/src/sql/doc/snippets/sqldatabase/sqldatabase_snippet.cpp @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [16] + QSqlQueryModel *model = new QSqlQueryModel; + model->setQuery("SELECT name, salary FROM employee"); + model->setHeaderData(0, Qt::Horizontal, tr("Name")); + model->setHeaderData(1, Qt::Horizontal, tr("Salary")); +//! [17] + QTableView *view = new QTableView; +//! [17] //! [18] + view->setModel(model); +//! [18] //! [19] + view->show(); +//! [16] //! [19] //! [20] + view->setEditTriggers(QAbstractItemView::NoEditTriggers); +//! [20] + } + +//! [21] + QSqlQueryModel model; + model.setQuery("SELECT name, salary FROM employee"); + int salary = model.record(4).value("salary").toInt(); +//! [21] + Q_UNUSED(salary); + + { +//! [22] + int salary = model.data(model.index(4, 1)).toInt(); +//! [22] + Q_UNUSED(salary); + } + + for (int row = 0; row < model.rowCount(); ++row) { + for (int col = 0; col < model.columnCount(); ++col) { + qDebug() << model.data(model.index(row, col)); + } + } +} + +class MyModel : public QSqlQueryModel +{ +public: + QVariant data(const QModelIndex &item, int role) const override; + void fetchModel(); + + int m_specialColumnNo; +}; + +//! [23] +QVariant MyModel::data(const QModelIndex &item, int role) const +{ + if (item.column() == m_specialColumnNo) { + // handle column separately + } + return QSqlQueryModel::data(item, role); +} +//! [23] + +void QSqlTableModel_snippets() +{ +//! [24] + QSqlTableModel *model = new QSqlTableModel; + model->setTable("employee"); + model->setEditStrategy(QSqlTableModel::OnManualSubmit); + model->select(); + model->setHeaderData(0, Qt::Horizontal, tr("Name")); + model->setHeaderData(1, Qt::Horizontal, tr("Salary")); + + QTableView *view = new QTableView; + view->setModel(model); + view->hideColumn(0); // don't show the ID + view->show(); +//! [24] + + { +//! [25] + QSqlTableModel model; + model.setTable("employee"); + model.select(); + int salary = model.record(4).value("salary").toInt(); +//! [25] + } +} + +void sql_intro_snippets() +{ + { +//! [26] + QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); + db.setHostName("bigblue"); + db.setDatabaseName("flightdb"); + db.setUserName("acarlson"); + db.setPassword("1uTbSbAs"); + bool ok = db.open(); +//! [26] + Q_UNUSED(ok); + } + + { +//! [27] + QSqlDatabase firstDB = QSqlDatabase::addDatabase("QMYSQL", "first"); + QSqlDatabase secondDB = QSqlDatabase::addDatabase("QMYSQL", "second"); +//! [27] + } + + { +//! [28] + QSqlDatabase defaultDB = QSqlDatabase::database(); +//! [28] //! [29] + QSqlDatabase firstDB = QSqlDatabase::database("first"); +//! [29] //! [30] + QSqlDatabase secondDB = QSqlDatabase::database("second"); +//! [30] + } + + { + // SELECT1 +//! [31] + QSqlQuery query; + query.exec("SELECT name, salary FROM employee WHERE salary > 50000"); +//! [31] + +//! [32] + while (query.next()) { + QString name = query.value(0).toString(); + int salary = query.value(1).toInt(); + qDebug() << name << salary; + } +//! [32] + } + + { + // FEATURE +//! [33] + QSqlQuery query; + int numRows; + query.exec("SELECT name, salary FROM employee WHERE salary > 50000"); + + QSqlDatabase defaultDB = QSqlDatabase::database(); + if (defaultDB.driver()->hasFeature(QSqlDriver::QuerySize)) { + numRows = query.size(); + } else { + // this can be very slow + query.last(); + numRows = query.at() + 1; + } +//! [33] + } + + { + // INSERT1 +//! [34] + QSqlQuery query; + query.exec("INSERT INTO employee (id, name, salary) " + "VALUES (1001, 'Thad Beaumont', 65000)"); +//! [34] + } + + { + // NAMED BINDING +//! [35] + QSqlQuery query; + query.prepare("INSERT INTO employee (id, name, salary) " + "VALUES (:id, :name, :salary)"); + query.bindValue(":id", 1001); + query.bindValue(":name", "Thad Beaumont"); + query.bindValue(":salary", 65000); + query.exec(); +//! [35] + } + + { + // POSITIONAL BINDING +//! [36] + QSqlQuery query; + query.prepare("INSERT INTO employee (id, name, salary) " + "VALUES (?, ?, ?)"); + query.addBindValue(1001); + query.addBindValue("Thad Beaumont"); + query.addBindValue(65000); + query.exec(); +//! [36] + } + + { + // UPDATE1 +//! [37] + QSqlQuery query; + query.exec("UPDATE employee SET salary = 70000 WHERE id = 1003"); +//! [37] + } + + { + // DELETE1 +//! [38] + QSqlQuery query; + query.exec("DELETE FROM employee WHERE id = 1007"); +//! [38] + } + + { + // TRANSACTION +//! [39] + QSqlDatabase::database().transaction(); + QSqlQuery query; + query.exec("SELECT id FROM employee WHERE name = 'Torild Halvorsen'"); + if (query.next()) { + int employeeId = query.value(0).toInt(); + query.exec("INSERT INTO project (id, name, ownerid) " + "VALUES (201, 'Manhattan Project', " + + QString::number(employeeId) + ')'); + } + QSqlDatabase::database().commit(); +//! [39] + } + + { + // SQLQUERYMODEL1 +//! [40] + QSqlQueryModel model; + model.setQuery("SELECT * FROM employee"); + + for (int i = 0; i < model.rowCount(); ++i) { + int id = model.record(i).value("id").toInt(); + QString name = model.record(i).value("name").toString(); + qDebug() << id << name; + } +//! [40] + } + + { + // SQLTABLEMODEL1 +//! [41] + QSqlTableModel model; + model.setTable("employee"); + model.setFilter("salary > 50000"); + model.setSort(2, Qt::DescendingOrder); + model.select(); + + for (int i = 0; i < model.rowCount(); ++i) { + QString name = model.record(i).value("name").toString(); + int salary = model.record(i).value("salary").toInt(); + qDebug() << name << salary; + } +//! [41] + } + + { + // SQLTABLEMODEL2 + QSqlTableModel model; + model.setTable("employee"); + +//! [42] + for (int i = 0; i < model.rowCount(); ++i) { + QSqlRecord record = model.record(i); + double salary = record.value("salary").toInt(); + salary *= 1.1; + record.setValue("salary", salary); + model.setRecord(i, record); + } + model.submitAll(); +//! [42] + + // SQLTABLEMODEL3 + int row = 1; + int column = 2; +//! [43] + model.setData(model.index(row, column), 75000); + model.submitAll(); +//! [43] + + // SQLTABLEMODEL4 +//! [44] + model.insertRows(row, 1); + model.setData(model.index(row, 0), 1013); + model.setData(model.index(row, 1), "Peter Gordon"); + model.setData(model.index(row, 2), 68500); + model.submitAll(); +//! [44] + +//! [45] + model.removeRows(row, 5); +//! [45] //! [46] + model.submitAll(); +//! [46] + } +} + +//! [47] +class XyzResult : public QSqlResult +{ +public: + XyzResult(const QSqlDriver *driver) + : QSqlResult(driver) {} + ~XyzResult() {} + +protected: + QVariant data(int /* index */) override { return QVariant(); } + bool isNull(int /* index */) override { return false; } + bool reset(const QString & /* query */) override { return false; } + bool fetch(int /* index */) override { return false; } + bool fetchFirst() override { return false; } + bool fetchLast() override { return false; } + int size() override { return 0; } + int numRowsAffected() override { return 0; } + QSqlRecord record() const override { return QSqlRecord(); } +}; +//! [47] + +//! [48] +class XyzDriver : public QSqlDriver +{ +public: + XyzDriver() {} + ~XyzDriver() {} + + bool hasFeature(DriverFeature /* feature */) const override { return false; } + bool open(const QString & /* db */, const QString & /* user */, + const QString & /* password */, const QString & /* host */, + int /* port */, const QString & /* options */) override + { return false; } + void close() {} + QSqlResult *createResult() const override { return new XyzResult(this); } +}; +//! [48] + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + QSqlDatabase_snippets(); + QSqlField_snippets(); + QSqlQuery_snippets(); + QSqlQueryModel_snippets(); + QSqlTableModel_snippets(); + + XyzDriver driver; + XyzResult result(&driver); +} diff --git a/src/sql/doc/src/sql-driver.qdoc b/src/sql/doc/src/sql-driver.qdoc index 7736591ae9..9ec50c0622 100644 --- a/src/sql/doc/src/sql-driver.qdoc +++ b/src/sql/doc/src/sql-driver.qdoc @@ -459,7 +459,7 @@ SQL result must get a new handle after each call to any of QSqlResult fetch functions. Example: - \snippet code/doc_src_sql-driver.cpp 36 + \snippet code/doc_src_sql-driver_snippet.cpp 36 While reading the results of a forward-only query with PostgreSQL, the database connection cannot be used to execute other queries. diff --git a/src/sql/doc/src/sql-programming.qdoc b/src/sql/doc/src/sql-programming.qdoc index f20b1292fc..56bb48b27b 100644 --- a/src/sql/doc/src/sql-programming.qdoc +++ b/src/sql/doc/src/sql-programming.qdoc @@ -480,15 +480,15 @@ The following example creates a view based on an SQL data model: - \snippet sqldatabase/sqldatabase.cpp 17 - \snippet sqldatabase/sqldatabase.cpp 18 - \snippet sqldatabase/sqldatabase.cpp 19 + \snippet sqldatabase/sqldatabase_snippet.cpp 17 + \snippet sqldatabase/sqldatabase_snippet.cpp 18 + \snippet sqldatabase/sqldatabase_snippet.cpp 19 If the model is a read-write model (e.g., QSqlTableModel), the view lets the user edit the fields. You can disable this by calling - \snippet sqldatabase/sqldatabase.cpp 20 + \snippet sqldatabase/sqldatabase_snippet.cpp 20 You can use the same model as a data source for multiple views. If the user edits the model through one of the views, the other diff --git a/src/sql/kernel/qsqldatabase.cpp b/src/sql/kernel/qsqldatabase.cpp index 32338c1fe2..facf7fd28c 100644 --- a/src/sql/kernel/qsqldatabase.cpp +++ b/src/sql/kernel/qsqldatabase.cpp @@ -561,7 +561,7 @@ QStringList QSqlDatabase::drivers() and don't want to compile it as a plugin. Example: - \snippet code/src_sql_kernel_qsqldatabase.cpp 2 + \snippet code/src_sql_kernel_qsqldatabase_snippet.cpp 2 QSqlDatabase takes ownership of the \a creator pointer, so you mustn't delete it yourself. @@ -1271,9 +1271,7 @@ bool QSqlDatabase::isDriverAvailable(const QString& name) application. For example, you can create a PostgreSQL connection with your own QPSQL driver like this: - \snippet code/src_sql_kernel_qsqldatabase.cpp 5 - \codeline - \snippet code/src_sql_kernel_qsqldatabase.cpp 6 + \snippet code/src_sql_kernel_qsqldatabase_snippet.cpp 6 The above code sets up a PostgreSQL connection and instantiates a QPSQLDriver object. Next, addDatabase() is called to add the @@ -1292,7 +1290,7 @@ bool QSqlDatabase::isDriverAvailable(const QString& name) client library. Make sure the client library is in your linker's search path, and add lines like these to your \c{.pro} file: - \snippet code/src_sql_kernel_qsqldatabase.cpp 7 + \snippet code/src_sql_kernel_qsqldatabase_snippet.cpp 7 The method described works for all the supplied drivers. The only difference will be in the driver constructor arguments. Here is a diff --git a/src/sql/kernel/qsqlquery.cpp b/src/sql/kernel/qsqlquery.cpp index 34a3ba3755..32c6166c79 100644 --- a/src/sql/kernel/qsqlquery.cpp +++ b/src/sql/kernel/qsqlquery.cpp @@ -414,7 +414,7 @@ bool QSqlQuery::exec(const QString& query) The fields are numbered from left to right using the text of the \c SELECT statement, e.g. in - \snippet code/src_sql_kernel_qsqlquery.cpp 0 + \snippet code/src_sql_kernel_qsqlquery_snippet.cpp 0 field 0 is \c forename and field 1 is \c surname. Using \c{SELECT *} is not recommended because the order @@ -1044,7 +1044,7 @@ bool QSqlQuery::exec() The example above inserts four new rows into \c myTable: - \snippet code/src_sql_kernel_qsqlquery.cpp 3 + \snippet code/src_sql_kernel_qsqlquery_snippet.cpp 3 To bind NULL values, a null QVariant of the relevant type has to be added to the bound QVariantList; for example, \c diff --git a/src/sql/kernel/qsqlresult.cpp b/src/sql/kernel/qsqlresult.cpp index a41b3d8424..69c9dcbac9 100644 --- a/src/sql/kernel/qsqlresult.cpp +++ b/src/sql/kernel/qsqlresult.cpp @@ -1022,7 +1022,7 @@ bool QSqlResult::nextResult() This snippet returns the handle for PostgreSQL or MySQL: - \snippet code/src_sql_kernel_qsqlresult.cpp 2 + \snippet code/src_sql_kernel_qsqlresult_snippet.cpp 2 \sa QSqlDriver::handle() */ diff --git a/src/sql/models/qsqlquerymodel.cpp b/src/sql/models/qsqlquerymodel.cpp index 31d0ec985d..6d4e2c09c1 100644 --- a/src/sql/models/qsqlquerymodel.cpp +++ b/src/sql/models/qsqlquerymodel.cpp @@ -113,7 +113,7 @@ int QSqlQueryModelPrivate::columnInQuery(int modelColumn) const the lower-level QSqlQuery and can be used to provide data to view classes such as QTableView. For example: - \snippet sqldatabase/sqldatabase.cpp 16 + \snippet sqldatabase/sqldatabase_snippet.cpp 16 We set the model's query, then we set up the labels displayed in the view header. diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 7d2421d34e..4ce99e2968 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -211,7 +211,7 @@ bool QSqlTableModelPrivate::exec(const QString &stmt, bool prepStatement, lower-level QSqlQuery and can be used to provide data to view classes such as QTableView. For example: - \snippet sqldatabase/sqldatabase.cpp 24 + \snippet sqldatabase/sqldatabase_snippet.cpp 24 We set the SQL table's name and the edit strategy, then we set up the labels displayed in the view header. The edit strategy diff --git a/src/src.pro b/src/src.pro index 592f0cf644..6658cbc9e0 100644 --- a/src/src.pro +++ b/src/src.pro @@ -172,6 +172,13 @@ qtConfig(network) { qtConfig(sql) { SUBDIRS += src_sql src_plugins.depends += src_sql + + contains(QT_CONFIG, private_tests) { + src_sql_doc_snippets.subdir = sql/doc/snippets + src_sql_doc_snippets.target = sub-sql-doc-snippets + src_sql_doc_snippets.depends = src_sql + SUBDIRS += src_sql_doc_snippets + } } qtConfig(xml): SUBDIRS += src_xml qtConfig(testlib): SUBDIRS += src_testlib -- cgit v1.2.3 From 40f4b3de1a7dc8fdce26893880f2d4ca962cf966 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Fri, 20 Mar 2020 02:29:14 +0100 Subject: qeasingcurve/tst_qeasingcurve: Fix for MinGW 8.1 x86 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test fails on MinGW 8.1 x86, but not on MinGW 8.1 x86_64. Task-number: QTQAINFRA-3304 Task-number: QTBUG-69947 Change-Id: Ie9a35bd6d5a8481028cd0ea426d1cf00bd7cf093 Reviewed-by: Tony Sarajärvi Reviewed-by: Friedemann Kleint --- tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp b/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp index 2a9c1e1e41..898ac86874 100644 --- a/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp +++ b/tests/auto/corelib/tools/qeasingcurve/tst_qeasingcurve.cpp @@ -428,9 +428,9 @@ void tst_QEasingCurve::setCustomType() QCOMPARE(curve.valueForProgress(0.15), 0.1); QCOMPARE(curve.valueForProgress(0.20), 0.2); QCOMPARE(curve.valueForProgress(0.25), 0.2); - // QTBUG-69947, MinGW 7.3 returns 0.2 + // QTBUG-69947, MinGW 7.3, 8.1 x86 returns 0.2 #if defined(Q_CC_MINGW) -#if !defined(__GNUC__) || __GNUC__ != 7 || __GNUC_MINOR__ < 3 +#if !defined(__GNUC__) || defined(__MINGW64__) QCOMPARE(curve.valueForProgress(0.30), 0.3); #endif #endif -- cgit v1.2.3 From e7cff5bca7aefaea63e80598babf6bd7917aa1c3 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Tue, 10 Mar 2020 14:37:15 +0100 Subject: Fix keypad navigation within a button group for push buttons Keypad navigation within a group should work for auto-exclusive buttons, or for checkable buttons that are in a button group. Since the code already tests whether the button should be treated like an exclusive (which implies checkable) button, use the result of that test when finding the candidate button to move focus to, and not only when actually changing the checked button and the focus. Change-Id: I4dc41a90d51a8304483046252ceff0ebfe2a2e52 Fixes: QTBUG-27151 Done-with: david.faure@kdab.com Reviewed-by: Volker Hilsheimer --- src/widgets/widgets/qabstractbutton.cpp | 4 +- .../widgets/qbuttongroup/tst_qbuttongroup.cpp | 68 ++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/widgets/widgets/qabstractbutton.cpp b/src/widgets/widgets/qabstractbutton.cpp index 7961d0a21b..badeec37ee 100644 --- a/src/widgets/widgets/qabstractbutton.cpp +++ b/src/widgets/widgets/qabstractbutton.cpp @@ -246,7 +246,7 @@ void QAbstractButtonPrivate::notifyChecked() void QAbstractButtonPrivate::moveFocus(int key) { - QList buttonList = queryButtonList();; + QList buttonList = queryButtonList(); #if QT_CONFIG(buttongroup) bool exclusive = group ? group->d_func()->exclusive : autoExclusive; #else @@ -266,7 +266,7 @@ void QAbstractButtonPrivate::moveFocus(int key) for (int i = 0; i < buttonList.count(); ++i) { QAbstractButton *button = buttonList.at(i); if (button != f && button->window() == f->window() && button->isEnabled() && !button->isHidden() && - (autoExclusive || (button->focusPolicy() & focus_flag) == focus_flag)) { + (exclusive || (button->focusPolicy() & focus_flag) == focus_flag)) { QRect buttonRect = button->rect().translated(button->mapToGlobal(QPoint(0,0))); QPoint p = buttonRect.center(); diff --git a/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp b/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp index 279fe49e3a..3b8bcf98c3 100644 --- a/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp +++ b/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp @@ -83,6 +83,7 @@ Q_OBJECT private slots: void arrowKeyNavigation(); + void keyNavigationPushButtons(); void exclusive(); void exclusiveWithActions(); void testSignals(); @@ -185,6 +186,73 @@ void tst_QButtonGroup::arrowKeyNavigation() QVERIFY(bt3.hasFocus()); } +/* + Test that tab and arrow key navigation through buttons + in an invisible button group works as expected. Tabbing + into the group should give focus to the checked button, + and arrow navigation should change the checked button and + move focus. +*/ +void tst_QButtonGroup::keyNavigationPushButtons() +{ + if (!qt_tab_all_widgets()) + QSKIP("This test requires full keyboard control to be enabled."); + + QDialog dlg(nullptr); + QLineEdit *le1 = new QLineEdit; + le1->setObjectName("le1"); + QPushButton *pb1 = new QPushButton("Exclusive 1"); + pb1->setObjectName("pb1"); + pb1->setCheckable(true); + pb1->setChecked(true); + QPushButton *pb2 = new QPushButton("Exclusive 2"); + pb2->setObjectName("pb2"); + pb2->setCheckable(true); + QPushButton *pb3 = new QPushButton("Exclusive 3"); + pb3->setObjectName("pb3"); + pb3->setCheckable(true); + QLineEdit *le2 = new QLineEdit; + le2->setObjectName("le2"); + + QVBoxLayout* layout = new QVBoxLayout(&dlg); + layout->addWidget(le1); + layout->addWidget(pb1); + layout->addWidget(pb2); + layout->addWidget(pb3); + layout->addWidget(le2); + + QButtonGroup *buttonGroup = new QButtonGroup; + buttonGroup->addButton(pb1); + buttonGroup->addButton(pb2); + buttonGroup->addButton(pb3); + + dlg.show(); + qApp->setActiveWindow(&dlg); + if (!QTest::qWaitForWindowActive(&dlg)) + QSKIP("Window activation failed, skipping test"); + + QVERIFY2(le1->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Tab); + QVERIFY2(pb1->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QVERIFY2(pb1->isChecked(), qPrintable(buttonGroup->checkedButton()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Down); + QVERIFY2(pb2->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QVERIFY2(pb2->isChecked(), qPrintable(buttonGroup->checkedButton()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Down); + QVERIFY2(pb3->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QVERIFY2(pb3->isChecked(), qPrintable(buttonGroup->checkedButton()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Up); + QVERIFY2(pb2->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QVERIFY2(pb2->isChecked(), qPrintable(buttonGroup->checkedButton()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Tab); + QVERIFY2(le2->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Backtab); + QVERIFY2(pb2->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QVERIFY2(pb2->isChecked(), qPrintable(buttonGroup->checkedButton()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Backtab); + QVERIFY2(le1->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); +} + void tst_QButtonGroup::exclusiveWithActions() { QDialog dlg(0); -- cgit v1.2.3 From 6c5b42b678b8e54cbb58f75d5d8abb50eff46dd9 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 13 Mar 2020 18:19:19 +0100 Subject: rhi: gl: Reduce state changes ...between setGraphicsPipeline() calls with different QRhiGraphicsPipeline instances within the same pass. Also adds similar logic for the GL_ARRAY_BUFFER (vertex buffers) as that is a hotspot as well. This is not intended to be a 100% avoiding any redundant call solution, but rather to take care of the most common hotspots, so that bad OpenGL implementations with a larger API call overhead will not cause us to regress compared to the direct OpenGL rendering path in Quick and Quick3D. What we have here reduces the number of GL calls in the view3d example by ~200 within one frame, which is a pretty good start. Task-number: QTBUG-78603 Change-Id: I6aeab9c1c43b148ea6ef05d0284990a996c7ba28 Reviewed-by: Andy Nichols --- src/gui/rhi/qrhigles2.cpp | 265 ++++++++++++++++++++++++++++++++------------ src/gui/rhi/qrhigles2_p_p.h | 87 ++++++++++++++- 2 files changed, 278 insertions(+), 74 deletions(-) diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 3ba83464d2..52c5c70d3f 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -1193,7 +1193,18 @@ void QRhiGles2::beginExternal(QRhiCommandBuffer *cb) } QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); + + if (cbD->recordingPass == QGles2CommandBuffer::ComputePass + && !cbD->computePassState.writtenResources.isEmpty()) + { + QGles2CommandBuffer::Command cmd; + cmd.cmd = QGles2CommandBuffer::Command::Barrier; + cmd.args.barrier.barriers = GL_ALL_BARRIER_BITS; + cbD->commands.append(cmd); + } + executeCommandBuffer(cbD); + cbD->resetCommands(); if (vao) @@ -1933,6 +1944,10 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) GLenum indexType = GL_UNSIGNED_SHORT; quint32 indexStride = sizeof(quint16); quint32 indexOffset = 0; + GLuint currentArrayBuffer = 0; + static const int TRACKED_ATTRIB_COUNT = 16; + bool enabledAttribArrays[TRACKED_ATTRIB_COUNT]; + memset(enabledAttribArrays, 0, sizeof(enabledAttribArrays)); for (const QGles2CommandBuffer::Command &cmd : qAsConst(cbD->commands)) { switch (cmd.cmd) { @@ -1965,8 +1980,10 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) { QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.stencilRef.ps); if (psD) { - f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), GLint(cmd.args.stencilRef.ref), psD->m_stencilReadMask); - f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), GLint(cmd.args.stencilRef.ref), psD->m_stencilReadMask); + const GLint ref = GLint(cmd.args.stencilRef.ref); + f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), ref, psD->m_stencilReadMask); + f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), ref, psD->m_stencilReadMask); + cbD->graphicsPassState.dynamic.stencilRef = ref; } else { qWarning("No graphics pipeline active for setStencilRef; ignored"); } @@ -1983,8 +2000,11 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) if (bindingIdx != cmd.args.bindVertexBuffer.binding) continue; - // we do not support more than one vertex buffer - f->glBindBuffer(GL_ARRAY_BUFFER, cmd.args.bindVertexBuffer.buffer); + if (cmd.args.bindVertexBuffer.buffer != currentArrayBuffer) { + currentArrayBuffer = cmd.args.bindVertexBuffer.buffer; + // we do not support more than one vertex buffer + f->glBindBuffer(GL_ARRAY_BUFFER, currentArrayBuffer); + } const QRhiVertexInputBinding *inputBinding = psD->m_vertexInputLayout.bindingAt(bindingIdx); const int stride = int(inputBinding->stride()); @@ -2031,7 +2051,11 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) quint32 ofs = it->offset() + cmd.args.bindVertexBuffer.offset; f->glVertexAttribPointer(GLuint(locationIdx), size, type, normalize, stride, reinterpret_cast(quintptr(ofs))); - f->glEnableVertexAttribArray(GLuint(locationIdx)); + if (locationIdx >= TRACKED_ATTRIB_COUNT || !enabledAttribArrays[locationIdx]) { + if (locationIdx < TRACKED_ATTRIB_COUNT) + enabledAttribArrays[locationIdx] = true; + f->glEnableVertexAttribArray(GLuint(locationIdx)); + } if (inputBinding->classification() == QRhiVertexInputBinding::PerInstance && caps.instancing) f->glVertexAttribDivisor(GLuint(locationIdx), GLuint(inputBinding->instanceStepRate())); } @@ -2102,7 +2126,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) } break; case QGles2CommandBuffer::Command::BindGraphicsPipeline: - executeBindGraphicsPipeline(cmd.args.bindGraphicsPipeline.ps); + executeBindGraphicsPipeline(cbD, QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindGraphicsPipeline.ps)); break; case QGles2CommandBuffer::Command::BindShaderResources: bindShaderResources(cmd.args.bindShaderResources.maybeGraphicsPs, @@ -2148,6 +2172,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT) f->glClearStencil(GLint(cmd.args.clear.s)); f->glClear(cmd.args.clear.mask); + cbD->graphicsPassState.reset(); // altered depth/color write, invalidate in order to avoid confusing the state tracking break; case QGles2CommandBuffer::Command::BufferSubData: f->glBindBuffer(cmd.args.bufferSubData.target, cmd.args.bufferSubData.buffer); @@ -2328,83 +2353,179 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) } } -void QRhiGles2::executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps) +void QRhiGles2::executeBindGraphicsPipeline(QGles2CommandBuffer *cbD, QGles2GraphicsPipeline *psD) { - QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps); + QGles2CommandBuffer::GraphicsPassState &state(cbD->graphicsPassState); + const bool forceUpdate = !state.valid; + state.valid = true; + + const bool scissor = psD->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor); + if (forceUpdate || scissor != state.scissor) { + state.scissor = scissor; + if (scissor) + f->glEnable(GL_SCISSOR_TEST); + else + f->glDisable(GL_SCISSOR_TEST); + } - // No state tracking logic as of now. Could introduce something to reduce - // the number of gl* calls (when using and changing between multiple - // pipelines), but then begin/endExternal() should invalidate the cached - // state as appropriate. + const bool cullFace = psD->m_cullMode != QRhiGraphicsPipeline::None; + const GLenum cullMode = cullFace ? toGlCullMode(psD->m_cullMode) : GL_NONE; + if (forceUpdate || cullFace != state.cullFace || cullMode != state.cullMode) { + state.cullFace = cullFace; + state.cullMode = cullMode; + if (cullFace) { + f->glEnable(GL_CULL_FACE); + f->glCullFace(cullMode); + } else { + f->glDisable(GL_CULL_FACE); + } + } - if (psD->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) - f->glEnable(GL_SCISSOR_TEST); - else - f->glDisable(GL_SCISSOR_TEST); - if (psD->m_cullMode == QRhiGraphicsPipeline::None) { - f->glDisable(GL_CULL_FACE); - } else { - f->glEnable(GL_CULL_FACE); - f->glCullFace(toGlCullMode(psD->m_cullMode)); + const GLenum frontFace = toGlFrontFace(psD->m_frontFace); + if (forceUpdate || frontFace != state.frontFace) { + state.frontFace = frontFace; + f->glFrontFace(frontFace); } - f->glFrontFace(toGlFrontFace(psD->m_frontFace)); + if (!psD->m_targetBlends.isEmpty()) { - const QRhiGraphicsPipeline::TargetBlend &blend(psD->m_targetBlends.first()); // no MRT - GLboolean wr = blend.colorWrite.testFlag(QRhiGraphicsPipeline::R); - GLboolean wg = blend.colorWrite.testFlag(QRhiGraphicsPipeline::G); - GLboolean wb = blend.colorWrite.testFlag(QRhiGraphicsPipeline::B); - GLboolean wa = blend.colorWrite.testFlag(QRhiGraphicsPipeline::A); - f->glColorMask(wr, wg, wb, wa); - if (blend.enable) { - f->glEnable(GL_BLEND); - f->glBlendFuncSeparate(toGlBlendFactor(blend.srcColor), - toGlBlendFactor(blend.dstColor), - toGlBlendFactor(blend.srcAlpha), - toGlBlendFactor(blend.dstAlpha)); - f->glBlendEquationSeparate(toGlBlendOp(blend.opColor), toGlBlendOp(blend.opAlpha)); - } else { - f->glDisable(GL_BLEND); + // We do not have MRT support here, meaning all targets use the blend + // params from the first one. This is technically incorrect, even if + // nothing in Qt relies on it. However, considering that + // glBlendFuncSeparatei is only available in GL 4.0+ and GLES 3.2+, we + // may just live with this for now because no point in bothering if it + // won't be usable on many GLES (3.1 or 3.0) systems. + const QRhiGraphicsPipeline::TargetBlend &targetBlend(psD->m_targetBlends.first()); + + const QGles2CommandBuffer::GraphicsPassState::ColorMask colorMask = { + targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::R), + targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::G), + targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::B), + targetBlend.colorWrite.testFlag(QRhiGraphicsPipeline::A) + }; + if (forceUpdate || colorMask != state.colorMask) { + state.colorMask = colorMask; + f->glColorMask(colorMask.r, colorMask.g, colorMask.b, colorMask.a); + } + + const bool blendEnabled = targetBlend.enable; + const QGles2CommandBuffer::GraphicsPassState::Blend blend = { + toGlBlendFactor(targetBlend.srcColor), + toGlBlendFactor(targetBlend.dstColor), + toGlBlendFactor(targetBlend.srcAlpha), + toGlBlendFactor(targetBlend.dstAlpha), + toGlBlendOp(targetBlend.opColor), + toGlBlendOp(targetBlend.opAlpha) + }; + if (forceUpdate || blendEnabled != state.blendEnabled || (blendEnabled && blend != state.blend)) { + state.blendEnabled = blendEnabled; + if (blendEnabled) { + state.blend = blend; + f->glEnable(GL_BLEND); + f->glBlendFuncSeparate(blend.srcColor, blend.dstColor, blend.srcAlpha, blend.dstAlpha); + f->glBlendEquationSeparate(blend.opColor, blend.opAlpha); + } else { + f->glDisable(GL_BLEND); + } } } else { - f->glDisable(GL_BLEND); - f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + const QGles2CommandBuffer::GraphicsPassState::ColorMask colorMask = { true, true, true, true }; + if (forceUpdate || colorMask != state.colorMask) { + state.colorMask = colorMask; + f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + const bool blendEnabled = false; + if (forceUpdate || blendEnabled != state.blendEnabled) { + state.blendEnabled = blendEnabled; + f->glDisable(GL_BLEND); + } } - if (psD->m_depthTest) - f->glEnable(GL_DEPTH_TEST); - else - f->glDisable(GL_DEPTH_TEST); - if (psD->m_depthWrite) - f->glDepthMask(GL_TRUE); - else - f->glDepthMask(GL_FALSE); - f->glDepthFunc(toGlCompareOp(psD->m_depthOp)); - if (psD->m_stencilTest) { - f->glEnable(GL_STENCIL_TEST); - f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), 0, psD->m_stencilReadMask); - f->glStencilOpSeparate(GL_FRONT, - toGlStencilOp(psD->m_stencilFront.failOp), - toGlStencilOp(psD->m_stencilFront.depthFailOp), - toGlStencilOp(psD->m_stencilFront.passOp)); - f->glStencilMaskSeparate(GL_FRONT, psD->m_stencilWriteMask); - f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), 0, psD->m_stencilReadMask); - f->glStencilOpSeparate(GL_BACK, - toGlStencilOp(psD->m_stencilBack.failOp), - toGlStencilOp(psD->m_stencilBack.depthFailOp), - toGlStencilOp(psD->m_stencilBack.passOp)); - f->glStencilMaskSeparate(GL_BACK, psD->m_stencilWriteMask); - } else { - f->glDisable(GL_STENCIL_TEST); + + const bool depthTest = psD->m_depthTest; + if (forceUpdate || depthTest != state.depthTest) { + state.depthTest = depthTest; + if (depthTest) + f->glEnable(GL_DEPTH_TEST); + else + f->glDisable(GL_DEPTH_TEST); } - if (psD->m_depthBias != 0 || !qFuzzyIsNull(psD->m_slopeScaledDepthBias)) { - f->glPolygonOffset(psD->m_slopeScaledDepthBias, psD->m_depthBias); - f->glEnable(GL_POLYGON_OFFSET_FILL); - } else { - f->glDisable(GL_POLYGON_OFFSET_FILL); + const bool depthWrite = psD->m_depthWrite; + if (forceUpdate || depthWrite != state.depthWrite) { + state.depthWrite = depthWrite; + f->glDepthMask(depthWrite); + } + + const GLenum depthFunc = toGlCompareOp(psD->m_depthOp); + if (forceUpdate || depthFunc != state.depthFunc) { + state.depthFunc = depthFunc; + f->glDepthFunc(depthFunc); } - if (psD->m_topology == QRhiGraphicsPipeline::Lines || psD->m_topology == QRhiGraphicsPipeline::LineStrip) - f->glLineWidth(psD->m_lineWidth); + const bool stencilTest = psD->m_stencilTest; + const GLuint stencilReadMask = psD->m_stencilReadMask; + const GLuint stencilWriteMask = psD->m_stencilWriteMask; + const QGles2CommandBuffer::GraphicsPassState::StencilFace stencilFront = { + toGlCompareOp(psD->m_stencilFront.compareOp), + toGlStencilOp(psD->m_stencilFront.failOp), + toGlStencilOp(psD->m_stencilFront.depthFailOp), + toGlStencilOp(psD->m_stencilFront.passOp) + }; + const QGles2CommandBuffer::GraphicsPassState::StencilFace stencilBack = { + toGlCompareOp(psD->m_stencilBack.compareOp), + toGlStencilOp(psD->m_stencilBack.failOp), + toGlStencilOp(psD->m_stencilBack.depthFailOp), + toGlStencilOp(psD->m_stencilBack.passOp) + }; + if (forceUpdate || stencilTest != state.stencilTest + || (stencilTest + && (stencilReadMask != state.stencilReadMask || stencilWriteMask != state.stencilWriteMask + || stencilFront != state.stencil[0] || stencilBack != state.stencil[1]))) + { + state.stencilTest = stencilTest; + if (stencilTest) { + state.stencilReadMask = stencilReadMask; + state.stencilWriteMask = stencilWriteMask; + state.stencil[0] = stencilFront; + state.stencil[1] = stencilBack; + + f->glEnable(GL_STENCIL_TEST); + + f->glStencilFuncSeparate(GL_FRONT, stencilFront.func, state.dynamic.stencilRef, stencilReadMask); + f->glStencilOpSeparate(GL_FRONT, stencilFront.failOp, stencilFront.zfailOp, stencilFront.zpassOp); + f->glStencilMaskSeparate(GL_FRONT, stencilWriteMask); + + f->glStencilFuncSeparate(GL_BACK, stencilBack.func, state.dynamic.stencilRef, stencilReadMask); + f->glStencilOpSeparate(GL_BACK, stencilBack.failOp, stencilBack.zfailOp, stencilBack.zpassOp); + f->glStencilMaskSeparate(GL_BACK, stencilWriteMask); + } else { + f->glDisable(GL_STENCIL_TEST); + } + } + + const bool polyOffsetFill = psD->m_depthBias != 0 || !qFuzzyIsNull(psD->m_slopeScaledDepthBias); + const float polyOffsetFactor = psD->m_slopeScaledDepthBias; + const float polyOffsetUnits = psD->m_depthBias; + if (forceUpdate || state.polyOffsetFill != polyOffsetFill + || polyOffsetFactor != state.polyOffsetFactor || polyOffsetUnits != state.polyOffsetUnits) + { + state.polyOffsetFill = polyOffsetFill; + state.polyOffsetFactor = polyOffsetFactor; + state.polyOffsetUnits = polyOffsetUnits; + if (polyOffsetFill) { + f->glPolygonOffset(polyOffsetFactor, polyOffsetUnits); + f->glEnable(GL_POLYGON_OFFSET_FILL); + } else { + f->glDisable(GL_POLYGON_OFFSET_FILL); + } + } + + if (psD->m_topology == QRhiGraphicsPipeline::Lines || psD->m_topology == QRhiGraphicsPipeline::LineStrip) { + const float lineWidth = psD->m_lineWidth; + if (forceUpdate || lineWidth != state.lineWidth) { + state.lineWidth = lineWidth; + f->glLineWidth(lineWidth); + } + } f->glUseProgram(psD->program); } @@ -2827,8 +2948,6 @@ void QRhiGles2::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch cbD->recordingPass = QGles2CommandBuffer::ComputePass; cbD->resetCachedState(); - - cbD->computePassState.reset(); } void QRhiGles2::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index 8b7d01532a..ac7b384cb6 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -522,6 +522,45 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer QRhiShaderResourceBindings *currentComputeSrb; uint currentSrbGeneration; + struct GraphicsPassState { + bool valid = false; + bool scissor; + bool cullFace; + GLenum cullMode; + GLenum frontFace; + bool blendEnabled; + struct ColorMask { bool r, g, b, a; } colorMask; + struct Blend { + GLenum srcColor; + GLenum dstColor; + GLenum srcAlpha; + GLenum dstAlpha; + GLenum opColor; + GLenum opAlpha; + } blend; + bool depthTest; + bool depthWrite; + GLenum depthFunc; + bool stencilTest; + GLuint stencilReadMask; + GLuint stencilWriteMask; + struct StencilFace { + GLenum func; + GLenum failOp; + GLenum zfailOp; + GLenum zpassOp; + } stencil[2]; // front, back + bool polyOffsetFill; + float polyOffsetFactor; + float polyOffsetUnits; + float lineWidth; + void reset() { valid = false; } + struct { + // not part of QRhiGraphicsPipeline but used by setGraphicsPipeline() + GLint stencilRef = 0; + } dynamic; + } graphicsPassState; + struct ComputePassState { enum Access { Read = 0x01, @@ -566,11 +605,57 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer currentGraphicsSrb = nullptr; currentComputeSrb = nullptr; currentSrbGeneration = 0; + graphicsPassState.reset(); + computePassState.reset(); } }; Q_DECLARE_TYPEINFO(QGles2CommandBuffer::Command, Q_MOVABLE_TYPE); +inline bool operator==(const QGles2CommandBuffer::GraphicsPassState::StencilFace &a, + const QGles2CommandBuffer::GraphicsPassState::StencilFace &b) +{ + return a.func == b.func + && a.failOp == b.failOp + && a.zfailOp == b.zfailOp + && a.zpassOp == b.zpassOp; +} + +inline bool operator!=(const QGles2CommandBuffer::GraphicsPassState::StencilFace &a, + const QGles2CommandBuffer::GraphicsPassState::StencilFace &b) +{ + return !(a == b); +} + +inline bool operator==(const QGles2CommandBuffer::GraphicsPassState::ColorMask &a, + const QGles2CommandBuffer::GraphicsPassState::ColorMask &b) +{ + return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; +} + +inline bool operator!=(const QGles2CommandBuffer::GraphicsPassState::ColorMask &a, + const QGles2CommandBuffer::GraphicsPassState::ColorMask &b) +{ + return !(a == b); +} + +inline bool operator==(const QGles2CommandBuffer::GraphicsPassState::Blend &a, + const QGles2CommandBuffer::GraphicsPassState::Blend &b) +{ + return a.srcColor == b.srcColor + && a.dstColor == b.dstColor + && a.srcAlpha == b.srcAlpha + && a.dstAlpha == b.dstAlpha + && a.opColor == b.opColor + && a.opAlpha == b.opAlpha; +} + +inline bool operator!=(const QGles2CommandBuffer::GraphicsPassState::Blend &a, + const QGles2CommandBuffer::GraphicsPassState::Blend &b) +{ + return !(a == b); +} + struct QGles2SwapChain : public QRhiSwapChain { QGles2SwapChain(QRhiImplementation *rhi); @@ -709,7 +794,7 @@ public: QRhiPassResourceTracker::TextureAccess access, QRhiPassResourceTracker::TextureStage stage); void executeCommandBuffer(QRhiCommandBuffer *cb); - void executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps); + void executeBindGraphicsPipeline(QGles2CommandBuffer *cbD, QGles2GraphicsPipeline *psD); void bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiComputePipeline *maybeComputePs, QRhiShaderResourceBindings *srb, const uint *dynOfsPairs, int dynOfsCount); -- cgit v1.2.3 From aa5855847c158524ceaa99f2e99fb2eba5b118af Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 18 Mar 2020 12:19:37 +0100 Subject: Add SSE2 optimized solid source composition Very similar to source-over, but have traditionally been inlined. Change-Id: I211f0b1c91c1a00c4769fbbfe2e3d0c7b22d7048 Reviewed-by: Eirik Aavitsland --- src/gui/painting/qdrawhelper.cpp | 6 ++++++ src/gui/painting/qdrawhelper_sse2.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 4b0cc2547c..39de1baa17 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -4521,6 +4521,10 @@ static void blend_color_argb(int count, const QSpan *spans, void *userData) uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x; if (spans->coverage == 255) { qt_memfill(target, color, spans->len); +#ifdef __SSE2__ + } else if (spans->len > 16) { + op.funcSolid(target, spans->len, color, spans->coverage); +#endif } else { uint c = BYTE_MUL(color, spans->coverage); int ialpha = 255 - spans->coverage; @@ -6764,10 +6768,12 @@ static void qInitDrawhelperFunctions() extern void QT_FASTCALL comp_func_SourceOver_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha); extern void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha); extern void QT_FASTCALL comp_func_Source_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha); + extern void QT_FASTCALL comp_func_solid_Source_sse2(uint *destPixels, int length, uint color, uint const_alpha); extern void QT_FASTCALL comp_func_Plus_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha); qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_sse2; qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_sse2; qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_sse2; + qt_functionForModeSolid_C[QPainter::CompositionMode_Source] = comp_func_solid_Source_sse2; qt_functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_sse2; #ifdef QT_COMPILER_SUPPORTS_SSSE3 diff --git a/src/gui/painting/qdrawhelper_sse2.cpp b/src/gui/painting/qdrawhelper_sse2.cpp index c82f41ec88..77b5ab42c5 100644 --- a/src/gui/painting/qdrawhelper_sse2.cpp +++ b/src/gui/painting/qdrawhelper_sse2.cpp @@ -319,6 +319,35 @@ void qt_memfill32_sse2(quint32 *dest, quint32 value, qsizetype count) } #endif // !__AVX2__ +void QT_FASTCALL comp_func_solid_Source_sse2(uint *destPixels, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) { + qt_memfill32(destPixels, color, length); + } else { + const quint32 ialpha = 255 - const_alpha; + color = BYTE_MUL(color, const_alpha); + int x = 0; + + quint32 *dst = (quint32 *) destPixels; + const __m128i colorVector = _mm_set1_epi32(color); + const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); + const __m128i half = _mm_set1_epi16(0x80); + const __m128i iAlphaVector = _mm_set1_epi16(ialpha); + + ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) + destPixels[x] = color + BYTE_MUL(destPixels[x], ialpha); + + for (; x < length-3; x += 4) { + __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); + BYTE_MUL_SSE2(dstVector, dstVector, iAlphaVector, colorMask, half); + dstVector = _mm_add_epi8(colorVector, dstVector); + _mm_store_si128((__m128i *)&dst[x], dstVector); + } + SIMD_EPILOGUE(x, length, 3) + destPixels[x] = color + BYTE_MUL(destPixels[x], ialpha); + } +} + void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha) { if ((const_alpha & qAlpha(color)) == 255) { -- cgit v1.2.3 From d535dfea1f2d801268396f328c5c99968ff164ec Mon Sep 17 00:00:00 2001 From: Mauro Persano Date: Fri, 13 Mar 2020 21:40:34 -0300 Subject: Doc: replace QFileInfo::created with birthTime Change-Id: Ia497febcbf28e4b3babe50fc6f05e12f3d686b91 Reviewed-by: Volker Hilsheimer --- src/corelib/io/qfileinfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp index 89834de29f..9f58458d4f 100644 --- a/src/corelib/io/qfileinfo.cpp +++ b/src/corelib/io/qfileinfo.cpp @@ -272,7 +272,7 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request) info objects, just append one to the file name given to the constructors or setFile(). - The file's dates are returned by created(), lastModified(), lastRead() and + The file's dates are returned by birthTime(), lastModified(), lastRead() and fileTime(). Information about the file's access permissions is obtained with isReadable(), isWritable() and isExecutable(). The file's ownership is available from owner(), ownerId(), group() and -- cgit v1.2.3 From 2af04860f6536bbbf82ee21d6aa95ca33a60fbf5 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 3 May 2019 20:02:10 +0200 Subject: qtimezoneprivate_tz: Apply a cache over the top of timezone data Constantly re-reading the timezone information only to be told the exact same thing is wildly expensive, which can hurt in operations that cause a lot of QTimeZone creation, for example, V4's DateObject - which creates them a lot (in DaylightSavingTA). This performance problem was identified when I noticed that a QDateTime binding updated once per frame was causing >100% CPU usage (on a desktop!) thanks to a QtQuickControls 1 Calendar (which has a number of bindings to the date's properties like getMonth() and so on). The newly added tst_QTimeZone::systemTimeZone benchmark gets a ~90% decrease in instruction count: --- before +++ after PASS : tst_QTimeZone::systemTimeZone() RESULT : tst_QTimeZone::systemTimeZone(): - 0.024 msecs per iteration (total: 51, iterations: 2048) + 0.0036 msecs per iteration (total: 59, iterations: 16384) Also impacted (over in QDateTime) is tst_QDateTime::setMSecsSinceEpochTz(). The results here are - on the surface - less impressive (~0.17% drop), however, it isn't even creating QTimeZone on a hot path to begin with, so a large drop would have been a surprise. Added several further benchmarks to cover non-system zones and traverse transitions. Done-With: Edward Welbourne Task-number: QTBUG-75585 Change-Id: I044a84fc2d3a2dc965f63cd3a3299fc509750bf7 Reviewed-by: Ulf Hermann Reviewed-by: Simon Hausmann --- src/corelib/time/qtimezoneprivate_p.h | 16 ++- src/corelib/time/qtimezoneprivate_tz.cpp | 141 ++++++++++++++--------- tests/benchmarks/corelib/time/qtimezone/main.cpp | 122 ++++++++++++++++++++ 3 files changed, 222 insertions(+), 57 deletions(-) diff --git a/src/corelib/time/qtimezoneprivate_p.h b/src/corelib/time/qtimezoneprivate_p.h index 5f6491ef81..c3e9064b8c 100644 --- a/src/corelib/time/qtimezoneprivate_p.h +++ b/src/corelib/time/qtimezoneprivate_p.h @@ -287,6 +287,16 @@ Q_DECL_CONSTEXPR inline bool operator==(const QTzTransitionRule &lhs, const QTzT Q_DECL_CONSTEXPR inline bool operator!=(const QTzTransitionRule &lhs, const QTzTransitionRule &rhs) noexcept { return !operator==(lhs, rhs); } +// These are stored separately from QTzTimeZonePrivate so that they can be +// cached, avoiding the need to re-parse them from disk constantly. +struct QTzTimeZoneCacheEntry +{ + QVector m_tranTimes; + QVector m_tranRules; + QList m_abbreviations; + QByteArray m_posixRule; +}; + class Q_AUTOTEST_EXPORT QTzTimeZonePrivate final : public QTimeZonePrivate { QTzTimeZonePrivate(const QTzTimeZonePrivate &) = default; @@ -334,13 +344,11 @@ private: QVector getPosixTransitions(qint64 msNear) const; Data dataForTzTransition(QTzTransitionTime tran) const; - QVector m_tranTimes; - QVector m_tranRules; - QList m_abbreviations; #if QT_CONFIG(icu) mutable QSharedDataPointer m_icu; #endif - QByteArray m_posixRule; + QTzTimeZoneCacheEntry cached_data; + QVector tranCache() const { return cached_data.m_tranTimes; } }; #endif // Q_OS_UNIX diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp index 3c2695a789..a94ed6b7a9 100644 --- a/src/corelib/time/qtimezoneprivate_tz.cpp +++ b/src/corelib/time/qtimezoneprivate_tz.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2019 Crimson AS ** Copyright (C) 2013 John Layt ** Contact: https://www.qt.io/licensing/ ** @@ -42,6 +43,7 @@ #include "private/qlocale_tools_p.h" #include +#include #include #include #include @@ -649,14 +651,26 @@ QTzTimeZonePrivate *QTzTimeZonePrivate::clone() const return new QTzTimeZonePrivate(*this); } -void QTzTimeZonePrivate::init(const QByteArray &ianaId) +class QTzTimeZoneCache +{ +public: + QTzTimeZoneCacheEntry fetchEntry(const QByteArray &ianaId); + +private: + QTzTimeZoneCacheEntry findEntry(const QByteArray &ianaId); + QHash m_cache; + QMutex m_mutex; +}; + +QTzTimeZoneCacheEntry QTzTimeZoneCache::findEntry(const QByteArray &ianaId) { + QTzTimeZoneCacheEntry ret; QFile tzif; if (ianaId.isEmpty()) { // Open system tz tzif.setFileName(QStringLiteral("/etc/localtime")); if (!tzif.open(QIODevice::ReadOnly)) - return; + return ret; } else { // Open named tz, try modern path first, if fails try legacy path tzif.setFileName(QLatin1String("/usr/share/zoneinfo/") + QString::fromLocal8Bit(ianaId)); @@ -669,9 +683,9 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId) if (PosixZone::parse(begin, zoneInfo.constEnd()).hasValidOffset() && (begin == zoneInfo.constEnd() || PosixZone::parse(begin, zoneInfo.constEnd()).hasValidOffset())) { - m_id = m_posixRule = ianaId; + ret.m_posixRule = ianaId; } - return; + return ret; } } } @@ -682,59 +696,59 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId) bool ok = false; QTzHeader hdr = parseTzHeader(ds, &ok); if (!ok || ds.status() != QDataStream::Ok) - return; + return ret; QVector tranList = parseTzTransitions(ds, hdr.tzh_timecnt, false); if (ds.status() != QDataStream::Ok) - return; + return ret; QVector typeList = parseTzTypes(ds, hdr.tzh_typecnt); if (ds.status() != QDataStream::Ok) - return; + return ret; QMap abbrevMap = parseTzAbbreviations(ds, hdr.tzh_charcnt, typeList); if (ds.status() != QDataStream::Ok) - return; + return ret; parseTzLeapSeconds(ds, hdr.tzh_leapcnt, false); if (ds.status() != QDataStream::Ok) - return; + return ret; typeList = parseTzIndicators(ds, typeList, hdr.tzh_ttisstdcnt, hdr.tzh_ttisgmtcnt); if (ds.status() != QDataStream::Ok) - return; + return ret; // If version 2 then parse the second block of data if (hdr.tzh_version == '2' || hdr.tzh_version == '3') { ok = false; QTzHeader hdr2 = parseTzHeader(ds, &ok); if (!ok || ds.status() != QDataStream::Ok) - return; + return ret; tranList = parseTzTransitions(ds, hdr2.tzh_timecnt, true); if (ds.status() != QDataStream::Ok) - return; + return ret; typeList = parseTzTypes(ds, hdr2.tzh_typecnt); if (ds.status() != QDataStream::Ok) - return; + return ret; abbrevMap = parseTzAbbreviations(ds, hdr2.tzh_charcnt, typeList); if (ds.status() != QDataStream::Ok) - return; + return ret; parseTzLeapSeconds(ds, hdr2.tzh_leapcnt, true); if (ds.status() != QDataStream::Ok) - return; + return ret; typeList = parseTzIndicators(ds, typeList, hdr2.tzh_ttisstdcnt, hdr2.tzh_ttisgmtcnt); if (ds.status() != QDataStream::Ok) - return; - m_posixRule = parseTzPosixRule(ds); + return ret; + ret.m_posixRule = parseTzPosixRule(ds); if (ds.status() != QDataStream::Ok) - return; + return ret; } // Translate the TZ file into internal format // Translate the array index based tz_abbrind into list index const int size = abbrevMap.size(); - m_abbreviations.clear(); - m_abbreviations.reserve(size); + ret.m_abbreviations.clear(); + ret.m_abbreviations.reserve(size); QVector abbrindList; abbrindList.reserve(size); for (auto it = abbrevMap.cbegin(), end = abbrevMap.cend(); it != end; ++it) { - m_abbreviations.append(it.value()); + ret.m_abbreviations.append(it.value()); abbrindList.append(it.key()); } for (int i = 0; i < typeList.size(); ++i) @@ -752,7 +766,7 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId) // Now for each transition time calculate and store our rule: const int tranCount = tranList.count();; - m_tranTimes.reserve(tranCount); + ret.m_tranTimes.reserve(tranCount); // The DST offset when in effect: usually stable, usually an hour: int lastDstOff = 3600; for (int i = 0; i < tranCount; i++) { @@ -806,24 +820,45 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId) rule.abbreviationIndex = tz_type.tz_abbrind; // If the rule already exist then use that, otherwise add it - int ruleIndex = m_tranRules.indexOf(rule); + int ruleIndex = ret.m_tranRules.indexOf(rule); if (ruleIndex == -1) { - m_tranRules.append(rule); - tran.ruleIndex = m_tranRules.size() - 1; + ret.m_tranRules.append(rule); + tran.ruleIndex = ret.m_tranRules.size() - 1; } else { tran.ruleIndex = ruleIndex; } tran.atMSecsSinceEpoch = tz_tran.tz_time * 1000; - m_tranTimes.append(tran); + ret.m_tranTimes.append(tran); } - if (m_tranTimes.isEmpty() && m_posixRule.isEmpty()) + + return ret; +} + +QTzTimeZoneCacheEntry QTzTimeZoneCache::fetchEntry(const QByteArray &ianaId) +{ + QMutexLocker locker(&m_mutex); + + // search the cache... + const auto& it = m_cache.find(ianaId); + if (it != m_cache.constEnd()) + return *it; + + // ... or build a new entry from scratch + QTzTimeZoneCacheEntry ret = findEntry(ianaId); + m_cache[ianaId] = ret; + return ret; +} + +void QTzTimeZonePrivate::init(const QByteArray &ianaId) +{ + static QTzTimeZoneCache tzCache; + const auto &entry = tzCache.fetchEntry(ianaId); + if (entry.m_tranTimes.isEmpty() && entry.m_posixRule.isEmpty()) return; // Invalid after all ! - if (ianaId.isEmpty()) - m_id = systemTimeZoneId(); - else - m_id = ianaId; + cached_data = std::move(entry); + m_id = ianaId.isEmpty() ? systemTimeZoneId() : ianaId; } QLocale::Country QTzTimeZonePrivate::country() const @@ -903,12 +938,12 @@ QString QTzTimeZonePrivate::displayName(QTimeZone::TimeType timeType, } // Otherwise is strange sequence, so work backwards through trans looking for first match, if any - auto it = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(), + auto it = std::partition_point(tranCache().cbegin(), tranCache().cend(), [currentMSecs](const QTzTransitionTime &at) { return at.atMSecsSinceEpoch <= currentMSecs; }); - while (it != m_tranTimes.cbegin()) { + while (it != tranCache().cbegin()) { --it; tran = dataForTzTransition(*it); int offset = tran.daylightTimeOffset; @@ -944,7 +979,7 @@ int QTzTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const bool QTzTimeZonePrivate::hasDaylightTime() const { // TODO Perhaps cache as frequently accessed? - for (const QTzTransitionRule &rule : m_tranRules) { + for (const QTzTransitionRule &rule : cached_data.m_tranRules) { if (rule.dstOffset != 0) return true; } @@ -960,11 +995,11 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::dataForTzTransition(QTzTransitionTime { QTimeZonePrivate::Data data; data.atMSecsSinceEpoch = tran.atMSecsSinceEpoch; - QTzTransitionRule rule = m_tranRules.at(tran.ruleIndex); + QTzTransitionRule rule = cached_data.m_tranRules.at(tran.ruleIndex); data.standardTimeOffset = rule.stdOffset; data.daylightTimeOffset = rule.dstOffset; data.offsetFromUtc = rule.stdOffset + rule.dstOffset; - data.abbreviation = QString::fromUtf8(m_abbreviations.at(rule.abbreviationIndex)); + data.abbreviation = QString::fromUtf8(cached_data.m_abbreviations.at(rule.abbreviationIndex)); return data; } @@ -972,37 +1007,37 @@ QVector QTzTimeZonePrivate::getPosixTransitions(qint64 m { const int year = QDateTime::fromMSecsSinceEpoch(msNear, Qt::UTC).date().year(); // The Data::atMSecsSinceEpoch of the single entry if zone is constant: - qint64 atTime = m_tranTimes.isEmpty() ? msNear : m_tranTimes.last().atMSecsSinceEpoch; - return calculatePosixTransitions(m_posixRule, year - 1, year + 1, atTime); + qint64 atTime = tranCache().isEmpty() ? msNear : tranCache().last().atMSecsSinceEpoch; + return calculatePosixTransitions(cached_data.m_posixRule, year - 1, year + 1, atTime); } QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const { // If the required time is after the last transition (or there were none) // and we have a POSIX rule, then use it: - if (!m_posixRule.isEmpty() - && (m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < forMSecsSinceEpoch)) { + if (!cached_data.m_posixRule.isEmpty() + && (tranCache().isEmpty() || tranCache().last().atMSecsSinceEpoch < forMSecsSinceEpoch)) { QVector posixTrans = getPosixTransitions(forMSecsSinceEpoch); auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(), [forMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) { return at.atMSecsSinceEpoch <= forMSecsSinceEpoch; }); // Use most recent, if any in the past; or the first if we have no other rules: - if (it > posixTrans.cbegin() || (m_tranTimes.isEmpty() && it < posixTrans.cend())) { + if (it > posixTrans.cbegin() || (tranCache().isEmpty() && it < posixTrans.cend())) { QTimeZonePrivate::Data data = *(it > posixTrans.cbegin() ? it - 1 : it); data.atMSecsSinceEpoch = forMSecsSinceEpoch; return data; } } - if (m_tranTimes.isEmpty()) // Only possible if !isValid() + if (tranCache().isEmpty()) // Only possible if !isValid() return invalidData(); // Otherwise, use the rule for the most recent or first transition: - auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(), + auto last = std::partition_point(tranCache().cbegin(), tranCache().cend(), [forMSecsSinceEpoch] (const QTzTransitionTime &at) { return at.atMSecsSinceEpoch <= forMSecsSinceEpoch; }); - if (last > m_tranTimes.cbegin()) + if (last > tranCache().cbegin()) --last; Data data = dataForTzTransition(*last); data.atMSecsSinceEpoch = forMSecsSinceEpoch; @@ -1018,8 +1053,8 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSince { // If the required time is after the last transition (or there were none) // and we have a POSIX rule, then use it: - if (!m_posixRule.isEmpty() - && (m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < afterMSecsSinceEpoch)) { + if (!cached_data.m_posixRule.isEmpty() + && (tranCache().isEmpty() || tranCache().last().atMSecsSinceEpoch < afterMSecsSinceEpoch)) { QVector posixTrans = getPosixTransitions(afterMSecsSinceEpoch); auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(), [afterMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) { @@ -1030,19 +1065,19 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSince } // Otherwise, if we can find a valid tran, use its rule: - auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(), + auto last = std::partition_point(tranCache().cbegin(), tranCache().cend(), [afterMSecsSinceEpoch] (const QTzTransitionTime &at) { return at.atMSecsSinceEpoch <= afterMSecsSinceEpoch; }); - return last != m_tranTimes.cend() ? dataForTzTransition(*last) : invalidData(); + return last != tranCache().cend() ? dataForTzTransition(*last) : invalidData(); } QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const { // If the required time is after the last transition (or there were none) // and we have a POSIX rule, then use it: - if (!m_posixRule.isEmpty() - && (m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < beforeMSecsSinceEpoch)) { + if (!cached_data.m_posixRule.isEmpty() + && (tranCache().isEmpty() || tranCache().last().atMSecsSinceEpoch < beforeMSecsSinceEpoch)) { QVector posixTrans = getPosixTransitions(beforeMSecsSinceEpoch); auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(), [beforeMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) { @@ -1051,15 +1086,15 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecs if (it > posixTrans.cbegin()) return *--it; // It fell between the last transition (if any) and the first of the POSIX rule: - return m_tranTimes.isEmpty() ? invalidData() : dataForTzTransition(m_tranTimes.last()); + return tranCache().isEmpty() ? invalidData() : dataForTzTransition(tranCache().last()); } // Otherwise if we can find a valid tran then use its rule - auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(), + auto last = std::partition_point(tranCache().cbegin(), tranCache().cend(), [beforeMSecsSinceEpoch] (const QTzTransitionTime &at) { return at.atMSecsSinceEpoch < beforeMSecsSinceEpoch; }); - return last > m_tranTimes.cbegin() ? dataForTzTransition(*--last) : invalidData(); + return last > tranCache().cbegin() ? dataForTzTransition(*--last) : invalidData(); } static long getSymloopMax() diff --git a/tests/benchmarks/corelib/time/qtimezone/main.cpp b/tests/benchmarks/corelib/time/qtimezone/main.cpp index 65455a7261..133e6451bc 100644 --- a/tests/benchmarks/corelib/time/qtimezone/main.cpp +++ b/tests/benchmarks/corelib/time/qtimezone/main.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2019 Crimson AS ** Copyright (C) 2018 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure ** Contact: https://www.qt.io/licensing/ ** @@ -30,14 +31,57 @@ #include #include +// Enable to test *every* zone, rather than a hand-picked few, in some _data() sets: +// #define EXHAUSTIVE + class tst_QTimeZone : public QObject { Q_OBJECT private Q_SLOTS: void isTimeZoneIdAvailable(); + void systemTimeZone(); + void zoneByName_data(); + void zoneByName(); + void transitionList_data(); + void transitionList(); + void transitionsForward_data() { transitionList_data(); } + void transitionsForward(); + void transitionsReverse_data() { transitionList_data(); } + void transitionsReverse(); }; +static QVector enoughZones() +{ +#ifdef EXHAUSTIVE + auto available = QTimeZone::availableTimeZoneIds(); + QVector result; + result.reserve(available.size() + 1); + for (conat auto &name : available) + result << name; +#else + QVector result{ + QByteArray("UTC"), + // Those named overtly in tst_QDateTime: + QByteArray("Europe/Oslo"), + QByteArray("America/Vancouver"), + QByteArray("Europe/Berlin"), + QByteArray("America/Sao_Paulo"), + QByteArray("Pacific/Auckland"), + QByteArray("Australia/Eucla"), + QByteArray("Asia/Kathmandu"), + QByteArray("Pacific/Kiritimati"), + QByteArray("Pacific/Apia"), + QByteArray("UTC+12:00"), + QByteArray("Australia/Sydney"), + QByteArray("Asia/Singapore"), + QByteArray("Australia/Brisbane") + }; +#endif + result << QByteArray("Vulcan/ShiKahr"); // invalid: also worth testing + return result; +} + void tst_QTimeZone::isTimeZoneIdAvailable() { const QList available = QTimeZone::availableTimeZoneIds(); @@ -47,6 +91,84 @@ void tst_QTimeZone::isTimeZoneIdAvailable() } } +void tst_QTimeZone::systemTimeZone() +{ + QBENCHMARK { + QTimeZone::systemTimeZone(); + } +} + +void tst_QTimeZone::zoneByName_data() +{ + QTest::addColumn("name"); + + const auto names = enoughZones(); + for (const auto &name : names) + QTest::newRow(name.constData()) << name; +} + +void tst_QTimeZone::zoneByName() +{ + QFETCH(QByteArray, name); + QTimeZone zone; + QBENCHMARK { + zone = QTimeZone(name); + } + Q_UNUSED(zone); +} + +void tst_QTimeZone::transitionList_data() +{ + QTest::addColumn("name"); + QTest::newRow("system") << QByteArray(); // Handled specially in the test. + + const auto names = enoughZones(); + for (const auto &name : names) { + QTimeZone zone(name); + if (zone.isValid() && zone.hasTransitions()) + QTest::newRow(name.constData()) << name; + } +} + +void tst_QTimeZone::transitionList() +{ + QFETCH(QByteArray, name); + const QTimeZone zone = name.isEmpty() ? QTimeZone::systemTimeZone() : QTimeZone(name); + const QDateTime early = QDate(1625, 6, 8).startOfDay(Qt::UTC); // Cassini's birth date + const QDateTime late // End of 32-bit signed time_t + = QDateTime::fromSecsSinceEpoch(std::numeric_limits::max(), Qt::UTC); + QTimeZone::OffsetDataList seq; + QBENCHMARK { + seq = zone.transitions(early, late); + } + Q_UNUSED(seq); +} + +void tst_QTimeZone::transitionsForward() +{ + QFETCH(QByteArray, name); + const QTimeZone zone = name.isEmpty() ? QTimeZone::systemTimeZone() : QTimeZone(name); + const QDateTime early = QDate(1625, 6, 8).startOfDay(Qt::UTC); // Cassini's birth date + QBENCHMARK { + QTimeZone::OffsetData tran = zone.nextTransition(early); + while (tran.atUtc.isValid()) + tran = zone.nextTransition(tran.atUtc); + } +} + +void tst_QTimeZone::transitionsReverse() +{ + QFETCH(QByteArray, name); + const QTimeZone zone = name.isEmpty() ? QTimeZone::systemTimeZone() : QTimeZone(name); + const QDateTime late // End of 32-bit signed time_t + = QDateTime::fromSecsSinceEpoch(std::numeric_limits::max(), Qt::UTC); + QBENCHMARK { + QTimeZone::OffsetData tran = zone.previousTransition(late); + while (tran.atUtc.isValid()) + tran = zone.previousTransition(tran.atUtc); + } +} + QTEST_MAIN(tst_QTimeZone) #include "main.moc" -- cgit v1.2.3 From d41d8297c8e74b8e4b684039c13e86b23cce11ea Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Thu, 19 Mar 2020 21:18:19 +0100 Subject: OpenSSL: bump the minimum supported version to 1.1.1 We don't support 1.0 any more, and 1.1.0 has reached EOL. Bump to 1.1.1 so we can freely use its APIs. [ChangeLog][QtNetwork][SSL] The minimum required version of OpenSSL is now 1.1.1. Change-Id: I5cfb6672fadfa48aedaefbcd6a954aa9eb85bfa5 Reviewed-by: Timur Pocheptsov Reviewed-by: Lars Knoll --- src/network/configure.json | 6 +++--- src/network/ssl/qsslsocket_openssl.cpp | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/network/configure.json b/src/network/configure.json index d3e5009f45..289d84fbb4 100644 --- a/src/network/configure.json +++ b/src/network/configure.json @@ -61,11 +61,11 @@ "export": "openssl", "test": { "tail": [ - "#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER-0 < 0x10100000L", - "# error OpenSSL >= 1.1.0 is required", + "#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER-0 < 0x10101000L", + "# error OpenSSL >= 1.1.1 is required", "#endif", "#if !defined(OPENSSL_NO_EC) && !defined(SSL_CTRL_SET_CURVES)", - "# error OpenSSL was reported as >= 1.1.0 but is missing required features, possibly it's libressl which is unsupported", + "# error OpenSSL was reported as >= 1.1.1 but is missing required features, possibly it's libressl which is unsupported", "#endif" ] }, diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index e197ce6515..9b28d52e21 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1942,6 +1942,12 @@ bool QSslSocketPrivate::ensureLibraryLoaded() // Initialize OpenSSL. if (q_OPENSSL_init_ssl(0, nullptr) != 1) return false; + + if (q_OpenSSL_version_num() < 0x10101000L) { + qCWarning(lcSsl, "QSslSocket: OpenSSL >= 1.1.1 is required; %s was found instead", q_OpenSSL_version(OPENSSL_VERSION)); + return false; + } + q_SSL_load_error_strings(); q_OpenSSL_add_all_algorithms(); -- cgit v1.2.3 From c798b286bdef4026b222e4e030faa36387e32d65 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 5 Mar 2020 07:17:45 -0800 Subject: Doc: update QJsonObject::operator[] non-const to say it creates key the non-const QJsonArray::operator[] requires a valid index and all the const operator[] say that it returns Undefined if the key or index doesn't exist. This is the exception. Task-number: QTBUG-39864 Change-Id: Iaa63461109844e978376fffd15f9716f0cc66cca Reviewed-by: Lars Knoll --- src/corelib/serialization/qjsonobject.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/corelib/serialization/qjsonobject.cpp b/src/corelib/serialization/qjsonobject.cpp index a6987279d3..aceb465920 100644 --- a/src/corelib/serialization/qjsonobject.cpp +++ b/src/corelib/serialization/qjsonobject.cpp @@ -401,7 +401,9 @@ QJsonValue QJsonObject::operator [](const QString &key) const #if QT_STRINGVIEW_LEVEL < 2 /*! - Returns a reference to the value for \a key. + Returns a reference to the value for \a key. If there is no value with key + \a key in the object, one is created with a QJsonValue::Null value and then + returned. The return value is of type QJsonValueRef, a helper class for QJsonArray and QJsonObject. When you get an object of type QJsonValueRef, you can -- cgit v1.2.3 From 30a0787907981da3811390735bf234068fc89944 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 5 Mar 2020 15:31:38 -0800 Subject: Fix binary compatibility issue in QJson{Array,Object} initializer_list The rewrite using CBOR internals replaced one of the two naked pointers that were members of QJsonArray and QJsonObject with a QExplicitlySharedDataPointer. The problem is that its operator= will read the current value to decrement the refcount and possibly delete the pointed object. But QJson{Array,Object}::initialize() are called from inlined code, without initialization. So we can't call operator=. We need to memcpy to write a nullptr. This is not unit-testable because it requires compiling against 5.14 or earlier, then running against 5.15. Fixes: QTBUG-82700 Change-Id: Iaa63461109844e978376fffd15f98c62656d197c Reviewed-by: Ulf Hermann --- src/corelib/serialization/qjsonarray.cpp | 7 +++++-- src/corelib/serialization/qjsonobject.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/corelib/serialization/qjsonarray.cpp b/src/corelib/serialization/qjsonarray.cpp index 08702771a8..05138ad610 100644 --- a/src/corelib/serialization/qjsonarray.cpp +++ b/src/corelib/serialization/qjsonarray.cpp @@ -167,7 +167,11 @@ QJsonArray::QJsonArray(QCborContainerPrivate *array) */ void QJsonArray::initialize() { - a = nullptr; + // Because we're being called with uninitialized state, we can't do: + // a = nullptr; + // QExplicitlyDataSharedPointer::operator= will read the current value + void *ptr = &a; + memset(ptr, 0, sizeof(a)); } /*! @@ -177,7 +181,6 @@ QJsonArray::~QJsonArray() = default; QJsonArray::QJsonArray(std::initializer_list args) { - initialize(); for (const auto & arg : args) append(arg); } diff --git a/src/corelib/serialization/qjsonobject.cpp b/src/corelib/serialization/qjsonobject.cpp index aceb465920..b76e50e2d2 100644 --- a/src/corelib/serialization/qjsonobject.cpp +++ b/src/corelib/serialization/qjsonobject.cpp @@ -150,7 +150,11 @@ QJsonObject::QJsonObject(QCborContainerPrivate *object) void QJsonObject::initialize() { - o = nullptr; + // Because we're being called with uninitialized state, we can't do: + // o = nullptr; + // QExplicitlyDataSharedPointer::operator= will read the current value + void *ptr = &o; + memset(ptr, 0, sizeof(o)); } /*! @@ -160,7 +164,6 @@ QJsonObject::~QJsonObject() = default; QJsonObject::QJsonObject(std::initializer_list > args) { - initialize(); for (const auto &arg : args) insert(arg.first, arg.second); } -- cgit v1.2.3 From 580e9eedf783ccbdcb67baa3d1a9dcdd53922f86 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Tue, 17 Mar 2020 18:07:15 +0100 Subject: QDateTimeEdit: with keyboardTracking off, allow values outside the range QDateTimeEdit very aggressively prevents user input that would result in values that are outside the dateTimeRange. While keyboardTracking is on this makes sense, as otherwise the dateTimeChanged signal would be emitted after each section, with a value that is outside the range. However, this prevented users from entering a date that is allowed, but where sections of the date are above or below the respective section in the maximum or minimum value. If keyboardTracking is off, QDateTimeEdit only emits the dateTimeChanged signal at the end of editing, when focus is lost or the return key is pressed, and then it enforces that the value is within the range anyway. This change makes the parser ignore the range during editing if keyboardTracking is off, thus allowing the user to enter a date where temporary values are outside the range. The test makes sure that we don't get signals emitted with out-of-range values, testing both with and without keyboard tracking. Change-Id: I00fb9f1b328a3477163f890c4618b40878657816 Fixes: QTBUG-65 Reviewed-by: Andy Shaw Reviewed-by: Lars Knoll --- src/widgets/widgets/qdatetimeedit_p.h | 14 ++- .../widgets/qdatetimeedit/tst_qdatetimeedit.cpp | 116 +++++++++++++++++++++ 2 files changed, 128 insertions(+), 2 deletions(-) diff --git a/src/widgets/widgets/qdatetimeedit_p.h b/src/widgets/widgets/qdatetimeedit_p.h index dcf8863c8b..323246a87b 100644 --- a/src/widgets/widgets/qdatetimeedit_p.h +++ b/src/widgets/widgets/qdatetimeedit_p.h @@ -94,8 +94,18 @@ public: // Override QDateTimeParser: QString displayText() const override { return edit->text(); } - QDateTime getMinimum() const override { return minimum.toDateTime(); } - QDateTime getMaximum() const override { return maximum.toDateTime(); } + QDateTime getMinimum() const override + { + if (keyboardTracking) + return minimum.toDateTime(); + return QDateTimeParser::getMinimum(); + } + QDateTime getMaximum() const override + { + if (keyboardTracking) + return maximum.toDateTime(); + return QDateTimeParser::getMaximum(); + } QLocale locale() const override { return q_func()->locale(); } QString getAmPmText(AmPm ap, Case cs) const override; int cursorPosition() const override { return edit ? edit->cursorPosition() : -1; } diff --git a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp index 4c6cf3588a..6345d238e1 100644 --- a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp +++ b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp @@ -195,6 +195,8 @@ private slots: void specialValueText(); void setRange_data(); void setRange(); + void editingRanged_data(); + void editingRanged(); void selectAndScrollWithKeys(); void backspaceKey(); @@ -1310,6 +1312,120 @@ void tst_QDateTimeEdit::setRange() } } +/* + Test that a user can input a date into a ranged QDateTimeEdit or QDateEdit + where a part of date is larger than the respective part of the maximum, or + smaller than the respective part of the minimum of the range. + + This test is expected to fail unless keyboard tracking of the edit is set + to off. Otherwise the changed-signal would be emitted with values outside + of the allowed range as the user types. +*/ +void tst_QDateTimeEdit::editingRanged_data() +{ + QTest::addColumn("minDate"); + QTest::addColumn("minTime"); + QTest::addColumn("maxDate"); + QTest::addColumn("maxTime"); + QTest::addColumn("userInput"); + QTest::addColumn("expected"); + + QTest::addRow("trivial") + << QDate(2010, 1, 1) << QTime(9, 0) + << QDate(2011, 12, 31) << QTime(16, 0) + << QString::fromLatin1("311220101600") + << QDateTime(QDate(2010, 12, 31), QTime(16, 0)); + + QTest::addRow("data0") + << QDate(2010, 12, 30) << QTime(16, 0) + << QDate(2011, 1, 2) << QTime(9, 0) + << QString::fromLatin1("311220102359") + << QDateTime(QDate(2010, 12, 31), QTime(23, 59)); + + QTest::addRow("data1") + << QDate(2010, 12, 30) << QTime(16, 0) + << QDate(2011, 1, 2) << QTime(9, 0) + << QString::fromLatin1("010120111823") + << QDateTime(QDate(2011, 1, 1), QTime(18, 23)); + + QTest::addRow("Out of range") + << QDate(2010, 12, 30) << QTime(16, 0) + << QDate(2011, 1, 2) << QTime(9, 0) + << QString::fromLatin1("090920111823") + << QDateTime(QDate(2011, 1, 2), QTime(9, 0)); + + QTest::addRow("only date") + << QDate(2010, 12, 30) << QTime() + << QDate(2011, 1, 2) << QTime() + << QString::fromLatin1("01012011") + << QDateTime(QDate(2011, 1, 1), QTime()); +} + +void tst_QDateTimeEdit::editingRanged() +{ + QFETCH(QDate, minDate); + QFETCH(QTime, minTime); + QFETCH(QDate, maxDate); + QFETCH(QTime, maxTime); + QFETCH(QString, userInput); + QFETCH(QDateTime, expected); + + QDateTimeEdit *edit; + if (minTime.isValid()) { + edit = new QDateTimeEdit; + edit->setDisplayFormat("dd.MM.yyyy hh:mm"); + edit->setDateTimeRange(QDateTime(minDate, minTime), QDateTime(maxDate, maxTime)); + } else { + edit = new QDateEdit; + edit->setDisplayFormat("dd.MM.yyyy"); + edit->setDateRange(minDate, maxDate); + } + + int callCount = 0; + connect(edit, &QDateTimeEdit::dateTimeChanged, [&](const QDateTime &dateTime) { + ++callCount; + if (minTime.isValid()) { + QVERIFY(dateTime >= QDateTime(minDate, minTime)); + QVERIFY(dateTime <= QDateTime(maxDate, maxTime)); + } else { + QVERIFY(dateTime.date() >= minDate); + QVERIFY(dateTime.date() <= maxDate); + } + }); + + edit->show(); + QApplication::setActiveWindow(edit); + if (!QTest::qWaitForWindowActive(edit)) + QSKIP("Failed to make window active, aborting"); + edit->setFocus(); + + // with keyboard tracking, never get a signal with an out-of-range value + edit->setKeyboardTracking(true); + QTest::keyClicks(edit, userInput); + QTest::keyClick(edit, Qt::Key_Return); + QVERIFY(callCount > 0); + + // QDateTimeEdit blocks these dates from being entered - see QTBUG-65 + QEXPECT_FAIL("data0", "Can't enter this date", Continue); + QEXPECT_FAIL("data1", "Can't enter this date", Continue); + QEXPECT_FAIL("Out of range", "Can't enter this date", Continue); + QEXPECT_FAIL("only date", "Can't enter this date", Continue); + QCOMPARE(edit->dateTime(), expected); + + // reset + edit->clearFocus(); + edit->setFocus(); + callCount = 0; + + edit->setKeyboardTracking(false); + QTest::keyClicks(edit, userInput); + QTest::keyClick(edit, Qt::Key_Return); + QCOMPARE(edit->dateTime(), expected); + QCOMPARE(callCount, 1); + + delete edit; +} + void tst_QDateTimeEdit::wrappingTime_data() { QTest::addColumn("startWithMin"); -- cgit v1.2.3 From bd75c87e0e76b328827a2aad1077f89e50e36d9d Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Wed, 18 Mar 2020 19:56:40 +0100 Subject: Doc: replace deprecated references to QGLWidget Remove references to the deprecated QGLWidget and replace it with QOpenGLWidget. Change-Id: Ia31df42ab61c25e9ce46f4491267d2c64910f55c Reviewed-by: Paul Wicking --- examples/opengl/doc/src/2dpainting.qdoc | 20 ++++++++++---------- examples/opengl/doc/src/cube.qdoc | 4 ++-- examples/widgets/doc/src/padnavigator.qdoc | 2 +- src/gui/painting/qpaintengine.cpp | 2 +- src/widgets/doc/snippets/graphicsview.cpp | 6 +++++- src/widgets/doc/src/graphicsview.qdoc | 8 ++++---- src/widgets/graphicsview/qgraphicsproxywidget.cpp | 2 +- src/widgets/graphicsview/qgraphicsscene.cpp | 2 +- src/widgets/graphicsview/qgraphicsview.cpp | 8 ++++---- 9 files changed, 29 insertions(+), 25 deletions(-) diff --git a/examples/opengl/doc/src/2dpainting.qdoc b/examples/opengl/doc/src/2dpainting.qdoc index e6b33e2a14..d39ae1d7ba 100644 --- a/examples/opengl/doc/src/2dpainting.qdoc +++ b/examples/opengl/doc/src/2dpainting.qdoc @@ -30,8 +30,8 @@ \title 2D Painting Example \ingroup examples-widgets-opengl - \brief The 2D Painting example shows how QPainter and QGLWidget can be used - together to display accelerated 2D graphics on supported hardware. + \brief The 2D Painting example shows how QPainter and QOpenGLWidget can be + used together to display accelerated 2D graphics on supported hardware. \image 2dpainting-example.png @@ -39,23 +39,23 @@ paint devices provided by QPaintDevice subclasses, such as QWidget and QImage. - Since QGLWidget is a subclass of QWidget, it is possible + Since QOpenGLWidget is a subclass of QWidget, it is possible to reimplement its \l{QWidget::paintEvent()}{paintEvent()} and use QPainter to draw on the device, just as you would with a QWidget. The only difference is that the painting operations will be accelerated in hardware if it is supported by your system's OpenGL drivers. In this example, we perform the same painting operations on a - QWidget and a QGLWidget. The QWidget is shown with anti-aliasing - enabled, and the QGLWidget will also use anti-aliasing if the + QWidget and a QOpenGLWidget. The QWidget is shown with anti-aliasing + enabled, and the QOpenGLWidget will also use anti-aliasing if the required extensions are supported by your system's OpenGL driver. \section1 Overview - To be able to compare the results of painting onto a QGLWidget subclass + To be able to compare the results of painting onto a QOpenGLWidget subclass with native drawing in a QWidget subclass, we want to show both kinds of widget side by side. To do this, we derive subclasses of QWidget and - QGLWidget, using a separate \c Helper class to perform the same painting + QOpenGLWidget, using a separate \c Helper class to perform the same painting operations for each, and lay them out in a top-level widget, itself provided a the \c Window class. @@ -63,7 +63,7 @@ In this example, the painting operations are performed by a helper class. We do this because we want the same painting operations to be performed - for both our QWidget subclass and the QGLWidget subclass. + for both our QWidget subclass and the QOpenGLWidget subclass. The \c Helper class is minimal: @@ -81,7 +81,7 @@ The actual painting is performed in the \c paint() function. This takes a QPainter that has already been set up to paint onto a paint device - (either a QWidget or a QGLWidget), a QPaintEvent that provides information + (either a QWidget or a QOpenGLWidget), a QPaintEvent that provides information about the region to be painted, and a measure of the elapsed time (in milliseconds) since the paint device was last updated. @@ -148,7 +148,7 @@ \section1 GLWidget Class Definition The \c GLWidget class definition is basically the same as the \c Widget - class except that it is derived from QGLWidget. + class except that it is derived from QOpenGLWidget. \snippet 2dpainting/glwidget.h 0 diff --git a/examples/opengl/doc/src/cube.qdoc b/examples/opengl/doc/src/cube.qdoc index 1fdfe0c6ec..d58b8d8020 100644 --- a/examples/opengl/doc/src/cube.qdoc +++ b/examples/opengl/doc/src/cube.qdoc @@ -47,7 +47,7 @@ The example consist of two classes: \list - \li \c MainWidget extends QGLWidget and contains OpenGL ES 2.0 + \li \c MainWidget extends QOpenGLWidget and contains OpenGL ES 2.0 initialization and drawing and mouse and timer event handling \li \c GeometryEngine handles polygon geometries. Transfers polygon geometry to vertex buffer objects and draws geometries from vertex buffer objects. @@ -92,7 +92,7 @@ \section1 Loading Textures from Qt Resource Files - \c QGLWidget interface implements methods for loading textures from QImage to GL + \c QOpenGLWidget interface implements methods for loading textures from QImage to GL texture memory. We still need to use OpenGL provided functions for specifying the GL texture unit and configuring texture filtering options. diff --git a/examples/widgets/doc/src/padnavigator.qdoc b/examples/widgets/doc/src/padnavigator.qdoc index d8e83978cf..31440b650f 100644 --- a/examples/widgets/doc/src/padnavigator.qdoc +++ b/examples/widgets/doc/src/padnavigator.qdoc @@ -545,7 +545,7 @@ with OpenGL, but without OpenGL it avoids unnecessary re-scaling of the background pixmap. \li It sets render hints that increase rendering quality. - \li If OpenGL is supported, a QGLWidget viewport is assigned to the view. + \li If OpenGL is supported, a QOpenGLWidget viewport is assigned to the view. \endlist Finally, we start the state engine. diff --git a/src/gui/painting/qpaintengine.cpp b/src/gui/painting/qpaintengine.cpp index 315bf0daf2..6b34911c15 100644 --- a/src/gui/painting/qpaintengine.cpp +++ b/src/gui/painting/qpaintengine.cpp @@ -158,7 +158,7 @@ QFont QTextItem::font() const X11 and \macos, it is the backend for painting on QImage and it is used as a fallback for paint engines that do not support a certain capability. In addition we provide QPaintEngine implementations for - OpenGL (accessible through QGLWidget) and printing (which allows using + OpenGL (accessible through QOpenGLWidget) and printing (which allows using QPainter to draw on a QPrinter object). If one wants to use QPainter to draw to a different backend, diff --git a/src/widgets/doc/snippets/graphicsview.cpp b/src/widgets/doc/snippets/graphicsview.cpp index 371cff24a4..9578f91eec 100644 --- a/src/widgets/doc/snippets/graphicsview.cpp +++ b/src/widgets/doc/snippets/graphicsview.cpp @@ -123,5 +123,9 @@ void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event) //! [6] QGraphicsView view(&scene); -view.setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); +QOpenGLWidget *gl = new QOpenGLWidget(); +QSurfaceFormat format; +format.setSamples(4); +gl->setFormat(format); +view.setViewport(gl); //! [6] diff --git a/src/widgets/doc/src/graphicsview.qdoc b/src/widgets/doc/src/graphicsview.qdoc index 33f480b0bf..94c824c2ed 100644 --- a/src/widgets/doc/src/graphicsview.qdoc +++ b/src/widgets/doc/src/graphicsview.qdoc @@ -117,7 +117,7 @@ of a scene. You can attach several views to the same scene, to provide several viewports into the same data set. The view widget is a scroll area, and provides scroll bars for navigating through large scenes. To - enable OpenGL support, you can set a QGLWidget as the viewport by + enable OpenGL support, you can set a QOpenGLWidget as the viewport by calling QGraphicsView::setViewport(). \snippet graphicsview.cpp 1 @@ -436,10 +436,10 @@ \section2 OpenGL Rendering - To enable OpenGL rendering, you simply set a new QGLWidget as the + To enable OpenGL rendering, you simply set a new QOpenGLWidget as the viewport of QGraphicsView by calling QGraphicsView::setViewport(). If - you want OpenGL with antialiasing, you need OpenGL sample buffer - support (see QGLFormat::sampleBuffers()). + you want OpenGL with antialiasing, you need to set a QSurfaceFormat + with the needed sample count (see QSurfaceFormat::setSamples()). Example: diff --git a/src/widgets/graphicsview/qgraphicsproxywidget.cpp b/src/widgets/graphicsview/qgraphicsproxywidget.cpp index fe3475e6bb..45720802d3 100644 --- a/src/widgets/graphicsview/qgraphicsproxywidget.cpp +++ b/src/widgets/graphicsview/qgraphicsproxywidget.cpp @@ -581,7 +581,7 @@ QGraphicsProxyWidget::~QGraphicsProxyWidget() Note that widgets with the Qt::WA_PaintOnScreen widget attribute set and widgets that wrap an external application or controller - cannot be embedded. Examples are QGLWidget and QAxWidget. + cannot be embedded. Examples are QOpenGLWidget and QAxWidget. \sa widget() */ diff --git a/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp index b669b0fe61..38c68b8fa5 100644 --- a/src/widgets/graphicsview/qgraphicsscene.cpp +++ b/src/widgets/graphicsview/qgraphicsscene.cpp @@ -2904,7 +2904,7 @@ QGraphicsSimpleTextItem *QGraphicsScene::addSimpleText(const QString &text, cons Note that widgets with the Qt::WA_PaintOnScreen widget attribute set and widgets that wrap an external application or controller - are not supported. Examples are QGLWidget and QAxWidget. + are not supported. Examples are QOpenGLWidget and QAxWidget. \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(), addText(), addSimpleText(), addItem() diff --git a/src/widgets/graphicsview/qgraphicsview.cpp b/src/widgets/graphicsview/qgraphicsview.cpp index 17c13e47d6..90bddf6e84 100644 --- a/src/widgets/graphicsview/qgraphicsview.cpp +++ b/src/widgets/graphicsview/qgraphicsview.cpp @@ -89,8 +89,8 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < By default, QGraphicsView provides a regular QWidget for the viewport widget. You can access this widget by calling viewport(), or you can replace it by calling setViewport(). To render using OpenGL, simply call - setViewport(new QGLWidget). QGraphicsView takes ownership of the viewport - widget. + setViewport(new QOpenGLWidget). QGraphicsView takes ownership of the + viewport widget. QGraphicsView supports affine transformations, using QTransform. You can either pass a matrix to setTransform(), or you can call one of the @@ -159,8 +159,8 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < fastest when QGraphicsView spends more time figuring out what to draw than it would spend drawing (e.g., when very many small items are repeatedly updated). This is the preferred update mode for viewports that do not - support partial updates, such as QGLWidget, and for viewports that need to - disable scroll optimization. + support partial updates, such as QOpenGLWidget, and for viewports that + need to disable scroll optimization. \value MinimalViewportUpdate QGraphicsView will determine the minimal viewport region that requires a redraw, minimizing the time spent drawing -- cgit v1.2.3 From d55dad94f984333c11a7f01fbd5d8c07e405129f Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 14 Mar 2020 14:37:11 -0500 Subject: 64-bit atomics: fix bad copy/paste in macro definitions Fixes: QTBUG-82864 Change-Id: I35d5bcc92b2e4bddaacbfffd15fc42d054fcb2b1 Reviewed-by: Mitch Curtis --- src/corelib/thread/qatomic_cxx11.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/corelib/thread/qatomic_cxx11.h b/src/corelib/thread/qatomic_cxx11.h index 7386aee126..9669554515 100644 --- a/src/corelib/thread/qatomic_cxx11.h +++ b/src/corelib/thread/qatomic_cxx11.h @@ -191,26 +191,26 @@ template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<2>::isLockFree() template<> struct QAtomicOpsSupport<8> { enum { IsSupported = 1 }; }; # define Q_ATOMIC_INT64_IS_SUPPORTED # if ATOMIC_LLONG_LOCK_FREE == 2 -# define Q_ATOMIC_INT16_REFERENCE_COUNTING_IS_ALWAYS_NATIVE -# define Q_ATOMIC_INT16_TEST_AND_SET_IS_ALWAYS_NATIVE -# define Q_ATOMIC_INT16_FETCH_AND_STORE_IS_ALWAYS_NATIVE -# define Q_ATOMIC_INT16_FETCH_AND_ADD_IS_ALWAYS_NATIVE +# define Q_ATOMIC_INT64_REFERENCE_COUNTING_IS_ALWAYS_NATIVE +# define Q_ATOMIC_INT64_TEST_AND_SET_IS_ALWAYS_NATIVE +# define Q_ATOMIC_INT64_FETCH_AND_STORE_IS_ALWAYS_NATIVE +# define Q_ATOMIC_INT64_FETCH_AND_ADD_IS_ALWAYS_NATIVE template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<8>::isLockFree() { return true; } # elif ATOMIC_LLONG_LOCK_FREE == 1 -# define Q_ATOMIC_INT16_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE -# define Q_ATOMIC_INT16_TEST_AND_SET_IS_SOMETIMES_NATIVE -# define Q_ATOMIC_INT16_FETCH_AND_STORE_IS_SOMETIMES_NATIVE -# define Q_ATOMIC_INT16_FETCH_AND_ADD_IS_SOMETIMES_NATIVE +# define Q_ATOMIC_INT64_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE +# define Q_ATOMIC_INT64_TEST_AND_SET_IS_SOMETIMES_NATIVE +# define Q_ATOMIC_INT64_FETCH_AND_STORE_IS_SOMETIMES_NATIVE +# define Q_ATOMIC_INT64_FETCH_AND_ADD_IS_SOMETIMES_NATIVE template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<8>::isLockFree() { return false; } # else -# define Q_ATOMIC_INT16_REFERENCE_COUNTING_IS_NEVER_NATIVE -# define Q_ATOMIC_INT16_TEST_AND_SET_IS_NEVER_NATIVE -# define Q_ATOMIC_INT16_FETCH_AND_STORE_IS_NEVER_NATIVE -# define Q_ATOMIC_INT16_FETCH_AND_ADD_IS_NEVER_NATIVE +# define Q_ATOMIC_INT64_REFERENCE_COUNTING_IS_NEVER_NATIVE +# define Q_ATOMIC_INT64_TEST_AND_SET_IS_NEVER_NATIVE +# define Q_ATOMIC_INT64_FETCH_AND_STORE_IS_NEVER_NATIVE +# define Q_ATOMIC_INT64_FETCH_AND_ADD_IS_NEVER_NATIVE template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<8>::isLockFree() { return false; } -- cgit v1.2.3 From 1e03015d551fb1542d43f54f8b64cf721da51913 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Tue, 10 Mar 2020 21:42:11 +0100 Subject: QTableView: Make sure to repaint all needed cells during resizing Don't try to update the scrollarea geometry while resizing a row or column since this may lead in flickering and artifacts, esp when the scroll mode is scrollPerItems. Instead do the calculation only when the resize is finished. Fixes: QTBUG-72870 Fixes: QTBUG-82595 Change-Id: Id6903c00485b3be6ed54bd5f9bcafdda6da21598 Reviewed-by: Friedemann Kleint Reviewed-by: Samuel Gaist Reviewed-by: Shawn Rutledge --- src/widgets/itemviews/qtableview.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/widgets/itemviews/qtableview.cpp b/src/widgets/itemviews/qtableview.cpp index 81fceba8dc..a8da6530a4 100644 --- a/src/widgets/itemviews/qtableview.cpp +++ b/src/widgets/itemviews/qtableview.cpp @@ -2939,14 +2939,17 @@ void QTableView::timerEvent(QTimerEvent *event) Q_D(QTableView); if (event->timerId() == d->columnResizeTimerID) { - updateGeometries(); - killTimer(d->columnResizeTimerID); - d->columnResizeTimerID = 0; + const int oldScrollMax = horizontalScrollBar()->maximum(); + if (horizontalHeader()->d_func()->state != QHeaderViewPrivate::ResizeSection) { + updateGeometries(); + killTimer(d->columnResizeTimerID); + d->columnResizeTimerID = 0; + } QRect rect; int viewportHeight = d->viewport->height(); int viewportWidth = d->viewport->width(); - if (d->hasSpans()) { + if (d->hasSpans() || horizontalScrollBar()->value() == oldScrollMax) { rect = QRect(0, 0, viewportWidth, viewportHeight); } else { for (int i = d->columnsToUpdate.size()-1; i >= 0; --i) { @@ -2964,14 +2967,17 @@ void QTableView::timerEvent(QTimerEvent *event) } if (event->timerId() == d->rowResizeTimerID) { - updateGeometries(); - killTimer(d->rowResizeTimerID); - d->rowResizeTimerID = 0; + const int oldScrollMax = verticalScrollBar()->maximum(); + if (verticalHeader()->d_func()->state != QHeaderViewPrivate::ResizeSection) { + updateGeometries(); + killTimer(d->rowResizeTimerID); + d->rowResizeTimerID = 0; + } int viewportHeight = d->viewport->height(); int viewportWidth = d->viewport->width(); int top; - if (d->hasSpans()) { + if (d->hasSpans() || verticalScrollBar()->value() == oldScrollMax) { top = 0; } else { top = viewportHeight; -- cgit v1.2.3 From cb509f3aeeea2a905b696f3914fa6d03c9106f71 Mon Sep 17 00:00:00 2001 From: Joni Poikelin Date: Mon, 17 Feb 2020 14:40:26 +0200 Subject: Doc: fix copy paste errors in border-*-style documentation Change-Id: I442513ec87e25610898c2102170a5f2bfec5ee77 Reviewed-by: Shawn Rutledge --- src/gui/doc/src/richtext.qdoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/doc/src/richtext.qdoc b/src/gui/doc/src/richtext.qdoc index 31a2ebf05b..1ef34455ab 100644 --- a/src/gui/doc/src/richtext.qdoc +++ b/src/gui/doc/src/richtext.qdoc @@ -1197,16 +1197,16 @@ \li none | dotted | dashed | dot-dash | dot-dot-dash | solid | double | groove | ridge | inset | outset \li Border style for text tables and table cells. \row \li \c border-top-style - \li + \li \li Top border style for table cells. \row \li \c border-bottom-style - \li + \li \li Bottom border style for table cells. \row \li \c border-left-style - \li + \li \li Left border style for table cells. \row \li \c border-right-style - \li + \li \li Right border style for table cells. \row \li \c border-width \li px -- cgit v1.2.3 From 50c6882e9f3fcf19a15f6adf0d45b9dfd731c624 Mon Sep 17 00:00:00 2001 From: Lorn Potter Date: Tue, 18 Feb 2020 09:24:06 +1000 Subject: wasm: Boost emscripten 1.39.8 as recommended MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ChangeLog][WebAssembly] Updated emscripten to version 1.39.8 Change-Id: I8d19f851eb0c0dd912792cee0db69e61cf4fd5f6 Reviewed-by: Morten Johan Sørvig --- mkspecs/features/wasm/emcc_ver.prf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkspecs/features/wasm/emcc_ver.prf b/mkspecs/features/wasm/emcc_ver.prf index 411f53e95d..3605651494 100644 --- a/mkspecs/features/wasm/emcc_ver.prf +++ b/mkspecs/features/wasm/emcc_ver.prf @@ -1,5 +1,5 @@ defineReplace(qtEmccRecommendedVersion) { - return (1.38.27) + return (1.39.8) } defineReplace(qtSystemEmccVersion) { -- cgit v1.2.3 From b4b8ffb2339d5bd889e3d60c7e264e10a561ab43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Thu, 19 Mar 2020 17:30:47 +0100 Subject: rhi: gles2: fix uniform gathering after struct-type member in UBOs Given: struct Light { vec4 foo; }; layout(std140, binding = 2) uniform material { Light light; int lightCount; }; the previous code would keep "light" appended for the prefix and look for `light.lightCount`. Change-Id: Ia8deacd0cb4833f45151e922fa7b5970169332eb Reviewed-by: Laszlo Agocs --- src/gui/rhi/qrhigles2.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 52c5c70d3f..395ae4b93f 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -3256,11 +3256,12 @@ void QRhiGles2::gatherUniforms(GLuint program, QByteArray prefix = ub.structName.toUtf8() + '.'; for (const QShaderDescription::BlockVariable &blockMember : ub.members) { if (blockMember.type == QShaderDescription::Struct) { - prefix += blockMember.name.toUtf8(); + QByteArray structPrefix = prefix + blockMember.name.toUtf8(); + const int baseOffset = blockMember.offset; if (blockMember.arrayDims.isEmpty()) { for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers) - registerUniformIfActive(structMember, prefix, ub.binding, baseOffset, program, dst); + registerUniformIfActive(structMember, structPrefix, ub.binding, baseOffset, program, dst); } else { if (blockMember.arrayDims.count() > 1) { qWarning("Array of struct '%s' has more than one dimension. Only the first dimension is used.", @@ -3270,7 +3271,7 @@ void QRhiGles2::gatherUniforms(GLuint program, const int elemSize = blockMember.size / dim; int elemOffset = baseOffset; for (int di = 0; di < dim; ++di) { - const QByteArray arrayPrefix = prefix + '[' + QByteArray::number(di) + ']' + '.'; + const QByteArray arrayPrefix = structPrefix + '[' + QByteArray::number(di) + ']' + '.'; for (const QShaderDescription::BlockVariable &structMember : blockMember.structMembers) registerUniformIfActive(structMember, arrayPrefix, ub.binding, elemOffset, program, dst); elemOffset += elemSize; -- cgit v1.2.3 From 94c564a0820105c29ca7e3267d0cf797032fd4cb Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 23 Mar 2020 09:16:48 +0100 Subject: Fusion Style: Use qobject_cast rather than dynamic_cast We want to avoid RTTI. Fixes: QTBUG-82287 Change-Id: Ib034094d4035d6dfa99d00ac198c93cc9572755b Reviewed-by: Christian Ehrlicher Reviewed-by: Friedemann Kleint --- src/widgets/styles/qfusionstyle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp index 751ce36bb3..3fbd127efe 100644 --- a/src/widgets/styles/qfusionstyle.cpp +++ b/src/widgets/styles/qfusionstyle.cpp @@ -479,7 +479,7 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, { QPixmap pixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/fusion_groupbox.png")); int topMargin = 0; - auto control = dynamic_cast(widget); + auto control = qobject_cast(widget); if (control && !control->isCheckable() && control->title().isEmpty()) { // Shrinking the topMargin if Not checkable AND title is empty topMargin = groupBoxTopMargin; -- cgit v1.2.3 From da1d6bccd2d7759e8e17872f823ef8b59bbefff3 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Mon, 23 Mar 2020 12:36:19 +0100 Subject: Fix the name in invokeMethod MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-82605 Change-Id: If21d9c25d607e8a363fe703b4ffa1e2d207eef39 Reviewed-by: Mårten Nordheim --- src/network/access/qnetworkreplyfileimpl.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp index afab8ffd94..68fac55feb 100644 --- a/src/network/access/qnetworkreplyfileimpl.cpp +++ b/src/network/access/qnetworkreplyfileimpl.cpp @@ -89,7 +89,7 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con // we handle only local files QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString()); setError(QNetworkReply::ProtocolInvalidOperationError, msg); - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection, Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError)); fileOpenFinished(false); return; @@ -134,7 +134,7 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con if (fi.isDir()) { QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url.toString()); setError(QNetworkReply::ContentOperationNotPermittedError, msg); - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + QMetaObject::invokeMethod(this, "errorOccured", Qt::QueuedConnection, Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentOperationNotPermittedError)); QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); return; @@ -149,11 +149,11 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con if (fi.exists()) { setError(QNetworkReply::ContentAccessDenied, msg); - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection, Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentAccessDenied)); } else { setError(QNetworkReply::ContentNotFoundError, msg); - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection, Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentNotFoundError)); } QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); -- cgit v1.2.3 From cc1d891b8ed1c4f23183e4b06f46e5840a993e35 Mon Sep 17 00:00:00 2001 From: Alexander Volkov Date: Mon, 16 Mar 2020 20:54:10 +0300 Subject: xcb: Add support for XdndActionList property This allows to pass and receive possible drop actions from other processes, including GTK applications. Fixes: QTBUG-75744 Change-Id: I944edc6fa00f8801a25912e70eb104a647a9fc0e Reviewed-by: JiDe Zhang Reviewed-by: Liang Qi --- src/plugins/platforms/xcb/qxcbatom.cpp | 1 + src/plugins/platforms/xcb/qxcbatom.h | 1 + src/plugins/platforms/xcb/qxcbdrag.cpp | 108 +++++++++++++++++++++++++++++++-- src/plugins/platforms/xcb/qxcbdrag.h | 15 ++++- 4 files changed, 118 insertions(+), 7 deletions(-) diff --git a/src/plugins/platforms/xcb/qxcbatom.cpp b/src/plugins/platforms/xcb/qxcbatom.cpp index d366564dd6..a73d28319d 100644 --- a/src/plugins/platforms/xcb/qxcbatom.cpp +++ b/src/plugins/platforms/xcb/qxcbatom.cpp @@ -182,6 +182,7 @@ static const char *xcb_atomnames = { "XdndActionCopy\0" "XdndActionLink\0" "XdndActionMove\0" + "XdndActionAsk\0" "XdndActionPrivate\0" // Xkb diff --git a/src/plugins/platforms/xcb/qxcbatom.h b/src/plugins/platforms/xcb/qxcbatom.h index 80b5887395..9cf93ec314 100644 --- a/src/plugins/platforms/xcb/qxcbatom.h +++ b/src/plugins/platforms/xcb/qxcbatom.h @@ -183,6 +183,7 @@ public: XdndActionCopy, XdndActionLink, XdndActionMove, + XdndActionAsk, XdndActionPrivate, // Xkb diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index 3d525598ca..d82129c6bc 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -216,6 +216,22 @@ void QXcbDrag::endDrag() initiatorWindow.clear(); } +Qt::DropAction QXcbDrag::defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const +{ + if (currentDrag() || drop_actions.isEmpty()) + return QBasicDrag::defaultAction(possibleActions, modifiers); + + return toDropAction(drop_actions.first()); +} + +void QXcbDrag::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) +{ + if (event->window != xdnd_dragsource || event->atom != atom(QXcbAtom::XdndActionList)) + return; + + readActionList(); +} + static bool windowInteractsWithPosition(xcb_connection_t *connection, const QPoint & pos, xcb_window_t w, xcb_shape_sk_t shapeType) { @@ -470,16 +486,20 @@ void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod move.data.data32[1] = 0; // flags move.data.data32[2] = (globalPos.x() << 16) + globalPos.y(); move.data.data32[3] = connection()->time(); - move.data.data32[4] = toXdndAction(defaultAction(currentDrag()->supportedActions(), mods)); + const auto supportedActions = currentDrag()->supportedActions(); + const auto requestedAction = defaultAction(supportedActions, mods); + move.data.data32[4] = toXdndAction(requestedAction); qCDebug(lcQpaXDnd) << "sending XdndPosition to target:" << target; source_time = connection()->time(); - if (w) + if (w) { handle_xdnd_position(w, &move, b, mods); - else + } else { + setActionList(requestedAction, supportedActions); xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move); + } } static const bool isUnity = qgetenv("XDG_CURRENT_DESKTOP").toLower() == "unity"; @@ -560,6 +580,16 @@ Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t a) const return Qt::CopyAction; } +Qt::DropActions QXcbDrag::toDropActions(const QVector &atoms) const +{ + Qt::DropActions actions; + for (const auto actionAtom : atoms) { + if (actionAtom != atom(QXcbAtom::XdndActionAsk)) + actions |= toDropAction(actionAtom); + } + return actions; +} + xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const { switch (a) { @@ -577,6 +607,60 @@ xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const } } +void QXcbDrag::readActionList() +{ + drop_actions.clear(); + auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, xdnd_dragsource, + atom(QXcbAtom::XdndActionList), XCB_ATOM_ATOM, + 0, 1024); + if (reply && reply->type != XCB_NONE && reply->format == 32) { + int length = xcb_get_property_value_length(reply.get()) / 4; + + xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply.get()); + for (int i = 0; i < length; ++i) + drop_actions.append(atoms[i]); + } +} + +void QXcbDrag::setActionList(Qt::DropAction requestedAction, Qt::DropActions supportedActions) +{ +#ifndef QT_NO_CLIPBOARD + QVector actions; + if (requestedAction != Qt::IgnoreAction) + actions.append(toXdndAction(requestedAction)); + + auto checkAppend = [this, requestedAction, supportedActions, &actions](Qt::DropAction action) { + if (requestedAction != action && supportedActions & action) + actions.append(toXdndAction(action)); + }; + + checkAppend(Qt::CopyAction); + checkAppend(Qt::MoveAction); + checkAppend(Qt::LinkAction); + + if (current_actions != actions) { + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->clipboard()->owner(), + atom(QXcbAtom::XdndActionList), + XCB_ATOM_ATOM, 32, actions.size(), actions.constData()); + current_actions = actions; + } +#endif +} + +void QXcbDrag::startListeningForActionListChanges() +{ + connection()->addWindowEventListener(xdnd_dragsource, this); + const uint32_t event_mask[] = { XCB_EVENT_MASK_PROPERTY_CHANGE }; + xcb_change_window_attributes(xcb_connection(), xdnd_dragsource, XCB_CW_EVENT_MASK, event_mask); +} + +void QXcbDrag::stopListeningForActionListChanges() +{ + const uint32_t event_mask[] = { XCB_EVENT_MASK_NO_EVENT }; + xcb_change_window_attributes(xcb_connection(), xdnd_dragsource, XCB_CW_EVENT_MASK, event_mask); + connection()->removeWindowEventListener(xdnd_dragsource); +} + int QXcbDrag::findTransactionByWindow(xcb_window_t window) { int at = -1; @@ -657,6 +741,9 @@ void QXcbDrag::handleEnter(QPlatformWindow *, const xcb_client_message_event_t * return; xdnd_dragsource = event->data.data32[0]; + startListeningForActionListChanges(); + readActionList(); + if (!proxy) proxy = xdndProxy(connection(), xdnd_dragsource); current_proxy_target = proxy ? proxy : xdnd_dragsource; @@ -723,7 +810,9 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message supported_actions = currentDrag()->supportedActions(); } else { dropData = m_dropData; - supported_actions = Qt::DropActions(toDropAction(e->data.data32[4])); + supported_actions = toDropActions(drop_actions); + if (e->data.data32[4] != atom(QXcbAtom::XdndActionAsk)) + supported_actions |= Qt::DropActions(toDropAction(e->data.data32[4])); } auto buttons = currentDrag() ? b : connection()->queryMouseButtons(); @@ -867,8 +956,10 @@ void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t // If the target receives XdndLeave, it frees any cached data and forgets the whole incident. qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndLeave"; - if (!currentWindow || w != currentWindow.data()->handle()) + if (!currentWindow || w != currentWindow.data()->handle()) { + stopListeningForActionListChanges(); return; // sanity + } // ### // if (checkEmbedded(current_embedding_widget, event)) { @@ -883,6 +974,8 @@ void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t event->data.data32[0], xdnd_dragsource); } + stopListeningForActionListChanges(); + QWindowSystemInterface::handleDrag(w->window(), nullptr, QPoint(), Qt::IgnoreAction, 0, 0); } @@ -929,6 +1022,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndDrop"; if (!currentWindow) { + stopListeningForActionListChanges(); xdnd_dragsource = 0; return; // sanity } @@ -951,7 +1045,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e supported_drop_actions = Qt::DropActions(l[4]); } else { dropData = m_dropData; - supported_drop_actions = accepted_drop_action; + supported_drop_actions = accepted_drop_action | toDropActions(drop_actions); } if (!dropData) @@ -986,6 +1080,8 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (char *)&finished); + stopListeningForActionListChanges(); + dropped = true; } diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h index 1388e68acc..b6371041e6 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.h +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -68,7 +68,7 @@ class QXcbScreen; class QDrag; class QShapedPixmapWindow; -class QXcbDrag : public QXcbObject, public QBasicDrag +class QXcbDrag : public QXcbObject, public QBasicDrag, public QXcbWindowEventListener { public: QXcbDrag(QXcbConnection *c); @@ -82,6 +82,10 @@ public: void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override; void endDrag() override; + Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const override; + + void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) override; + void handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy = 0); void handlePosition(QPlatformWindow *w, const xcb_client_message_event_t *event); void handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event); @@ -114,8 +118,14 @@ private: void send_leave(); Qt::DropAction toDropAction(xcb_atom_t atom) const; + Qt::DropActions toDropActions(const QVector &atoms) const; xcb_atom_t toXdndAction(Qt::DropAction a) const; + void readActionList(); + void setActionList(Qt::DropAction requestedAction, Qt::DropActions supportedActions); + void startListeningForActionListChanges(); + void stopListeningForActionListChanges(); + QPointer initiatorWindow; QPointer currentWindow; QPoint currentPosition; @@ -159,6 +169,9 @@ private: QVector drag_types; + QVector current_actions; + QVector drop_actions; + struct Transaction { xcb_timestamp_t timestamp; -- cgit v1.2.3 From 34fe5f75bcfd382b3928e221b5c9a002545fe3b9 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Mon, 23 Mar 2020 11:41:53 +0100 Subject: Deprecation warnings: replace qrand with QRandomGenerator Change-Id: I80fe6f611b367c6c8a1b77e82dc791493f88929b Reviewed-by: Timur Pocheptsov --- src/network/ssl/qsslsocket_qt.cpp | 3 ++- src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp | 4 +++- src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/network/ssl/qsslsocket_qt.cpp b/src/network/ssl/qsslsocket_qt.cpp index 9ff9a66c05..2f5d692162 100644 --- a/src/network/ssl/qsslsocket_qt.cpp +++ b/src/network/ssl/qsslsocket_qt.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include "qsslsocket_p.h" #include "qasn1element_p.h" @@ -138,7 +139,7 @@ static QByteArray _q_PKCS12_salt() QByteArray salt; salt.resize(8); for (int i = 0; i < salt.size(); ++i) - salt[i] = (qrand() & 0xff); + salt[i] = (QRandomGenerator::global()->generate() & 0xff); return salt; } diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp index 82b6d60bcd..aa91d9266b 100644 --- a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp +++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp @@ -37,6 +37,8 @@ ** ****************************************************************************/ +#include + #include #include #include @@ -504,7 +506,7 @@ static Picture getPatternFill(int screen, const QBrush &b) return X11->pattern_fills[i].picture; } // none found, replace one - int i = qrand() % 16; + int i = QRandomGenerator::global()->generate() % 16; if (X11->pattern_fills[i].screen != screen && X11->pattern_fills[i].picture) { XRenderFreePicture (QXcbX11Info::display(), X11->pattern_fills[i].picture); diff --git a/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp index 57b1882e4b..6a483fc7e5 100644 --- a/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp +++ b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp @@ -37,6 +37,8 @@ ** ****************************************************************************/ +#include + #include "qxcbconnection.h" #include "qcolormap_x11_p.h" #include "qxcbnativepainting.h" @@ -279,7 +281,7 @@ Picture QXcbX11Data::getSolidFill(int screen, const QColor &c) return X11->solid_fills[i].picture; } // none found, replace one - int i = qrand() % 16; + int i = QRandomGenerator::global()->generate() % 16; if (X11->solid_fills[i].screen != screen && X11->solid_fills[i].picture) { XRenderFreePicture (X11->display, X11->solid_fills[i].picture); -- cgit v1.2.3 From 6a972f44f3f88d6179e8e4eef54396b5808b3f4d Mon Sep 17 00:00:00 2001 From: Sze Howe Koh Date: Fri, 31 Jan 2020 19:54:03 +0800 Subject: QXcbCursor: Replace deprecated QCursor API Align with e79a6253813cd67954e371805da547c48ea32d3d in qtbase. Task-number: QTBUG-48701 Change-Id: Ia3ca2de6bbf4e9c1738f77193351dd95a14dd2dc Reviewed-by: Liang Qi --- src/plugins/platforms/xcb/qxcbcursor.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index 639e4f039c..42c7a52bd4 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -289,10 +289,10 @@ QXcbCursorCacheKey::QXcbCursorCacheKey(const QCursor &c) if (pixmapCacheKey) { bitmapCacheKey = pixmapCacheKey; } else { - Q_ASSERT(c.bitmap()); - Q_ASSERT(c.mask()); - bitmapCacheKey = c.bitmap()->cacheKey(); - maskCacheKey = c.mask()->cacheKey(); + Q_ASSERT(!c.bitmap(Qt::ReturnByValue).isNull()); + Q_ASSERT(!c.mask(Qt::ReturnByValue).isNull()); + bitmapCacheKey = c.bitmap(Qt::ReturnByValue).cacheKey(); + maskCacheKey = c.mask(Qt::ReturnByValue).cacheKey(); } } } @@ -613,8 +613,8 @@ xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor) qCWarning(lcQpaXcb, "xrender >= 0.5 required to create pixmap cursors"); } else { xcb_connection_t *conn = xcb_connection(); - xcb_pixmap_t cp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->bitmap()->toImage()); - xcb_pixmap_t mp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->mask()->toImage()); + xcb_pixmap_t cp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->bitmap(Qt::ReturnByValue).toImage()); + xcb_pixmap_t mp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->mask(Qt::ReturnByValue).toImage()); c = xcb_generate_id(conn); xcb_create_cursor(conn, c, cp, mp, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, spot.x(), spot.y()); -- cgit v1.2.3 From f10b31fc1be0697f65b13ee7ad966f0085f7ed00 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Wed, 18 Mar 2020 10:56:53 +0100 Subject: ANGLE: Fix resizing of windows (Take 2) Task-number: QTBUG-62475 Change-Id: I0ea17e7875906508941ae64bb396a4236928b0f9 Reviewed-by: Miguel Costa Reviewed-by: Friedemann Kleint --- .../libANGLE/renderer/d3d/d3d11/SwapChain11.cpp | 2 +- ...0017-ANGLE-Fix-resizing-of-windows-Take-2.patch | 27 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/angle/patches/0017-ANGLE-Fix-resizing-of-windows-Take-2.patch diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp index e8f13b388f..9ece77ecbc 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp @@ -845,7 +845,7 @@ EGLint SwapChain11::copyOffscreenToBackbuffer(const gl::Context *context, stateManager->setRenderTarget(mBackBufferRTView.get(), nullptr); // Set the viewport - stateManager->setSimpleViewport(mWidth, mHeight); + stateManager->setSimpleViewport(width, height); // Apply textures stateManager->setSimplePixelTextureAndSampler(mOffscreenSRView, mPassThroughSampler); diff --git a/src/angle/patches/0017-ANGLE-Fix-resizing-of-windows-Take-2.patch b/src/angle/patches/0017-ANGLE-Fix-resizing-of-windows-Take-2.patch new file mode 100644 index 0000000000..abab74b192 --- /dev/null +++ b/src/angle/patches/0017-ANGLE-Fix-resizing-of-windows-Take-2.patch @@ -0,0 +1,27 @@ +From 029d42d1049dcde7950c11fb9adf07c07a8c4c02 Mon Sep 17 00:00:00 2001 +From: Oliver Wolff +Date: Wed, 18 Mar 2020 10:56:53 +0100 +Subject: [PATCH] ANGLE: Fix resizing of windows (Take 2) + +Task-number: QTBUG-62475 +Change-Id: I0ea17e7875906508941ae64bb396a4236928b0f9 +--- + .../angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +index e8f13b388f..9ece77ecbc 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp +@@ -845,7 +845,7 @@ EGLint SwapChain11::copyOffscreenToBackbuffer(const gl::Context *context, + stateManager->setRenderTarget(mBackBufferRTView.get(), nullptr); + + // Set the viewport +- stateManager->setSimpleViewport(mWidth, mHeight); ++ stateManager->setSimpleViewport(width, height); + + // Apply textures + stateManager->setSimplePixelTextureAndSampler(mOffscreenSRView, mPassThroughSampler); +-- +2.20.1.windows.1 + -- cgit v1.2.3 From 99ace38d22c640e37bb1a41095ee3b126169816a Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Tue, 25 Feb 2020 14:18:56 +0100 Subject: CMake: Also import Qt plugins that are not literally named *Plugin There are some plugins in qtlocation, qtdeclarative whose names are not suffixed with 'Plugin', so the current logic fails to load the corresponding QtModule_XXX.cmake file. Work around this by just searching for Qt5$${CMAKE_MODULE_NAME}}_*.cmake. Users can define QT5_STRICT_PLUGIN_GLOB or Qt5$${CMAKE_MODULE_NAME}_STRICT_PLUGIN_GLOB to change back to the old behavior. [ChangeLog][cmake] Fixed an issue where some Qt location and declarative plugins whose name did not end with "Plugin" where not imported by the corresponding Qt component package. You can force the old behavior by setting QT5_STRICT_PLUGIN_GLOB or Qt5ModuleName_STRICT_PLUGIN_GLOB before the find_package(Qt5 ...) call. Fixes: QTBUG-58812 Change-Id: Ic8adf9562841ed49eabc4f7fb2b5ae257ca606cd Reviewed-by: Alexandru Croitor --- mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in index 4c3086a1f6..6eda017d75 100644 --- a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in +++ b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in @@ -566,8 +566,13 @@ if (NOT TARGET Qt5::$${CMAKE_MODULE_NAME}) !!ENDIF // TEMPLATE != aux !!IF isEmpty(CMAKE_INTERNAL_MODULE) - - file(GLOB pluginTargets \"${CMAKE_CURRENT_LIST_DIR}/Qt5$${CMAKE_MODULE_NAME}_*Plugin.cmake\") + # In Qt 5.15 the glob pattern was relaxed to also catch plugins not literally named "Plugin". + # Define QT5_STRICT_PLUGIN_GLOB or ModuleName_STRICT_PLUGIN_GLOB to revert to old behavior. + if (QT5_STRICT_PLUGIN_GLOB OR Qt5$${CMAKE_MODULE_NAME}_STRICT_PLUGIN_GLOB) + file(GLOB pluginTargets \"${CMAKE_CURRENT_LIST_DIR}/Qt5$${CMAKE_MODULE_NAME}_*Plugin.cmake\") + else() + file(GLOB pluginTargets \"${CMAKE_CURRENT_LIST_DIR}/Qt5$${CMAKE_MODULE_NAME}_*.cmake\") + endif() macro(_populate_$${CMAKE_MODULE_NAME}_plugin_properties Plugin Configuration PLUGIN_LOCATION IsDebugAndRelease) -- cgit v1.2.3 From 45a131d4657506feca6d23feec455d2dcf25fd1e Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Tue, 24 Mar 2020 08:30:33 +0100 Subject: Fix whitespace in Qt5XXConfig files Remove some unneeded empty lines, and fix indentation. Change-Id: Ie35e95f35f9625cc75070074be96bdeb62d5fd4c Reviewed-by: Alexandru Croitor --- mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in index 6eda017d75..59b34453f9 100644 --- a/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in +++ b/mkspecs/features/data/cmake/Qt5BasicConfig.cmake.in @@ -1,4 +1,3 @@ - if (CMAKE_VERSION VERSION_LESS 3.1.0) message(FATAL_ERROR \"Qt 5 $${CMAKE_MODULE_NAME} module requires at least CMake version 3.1.0\") endif() @@ -633,10 +632,8 @@ if (NOT TARGET Qt5::$${CMAKE_MODULE_NAME}) include(${pluginTarget}) endforeach() endif() - !!ENDIF // isEmpty(CMAKE_INTERNAL_MODULE) - !!IF !isEmpty(CMAKE_MODULE_EXTRAS) include(\"${CMAKE_CURRENT_LIST_DIR}/Qt5$${CMAKE_MODULE_NAME}ConfigExtras.cmake\") !!ENDIF @@ -645,6 +642,5 @@ if (NOT TARGET Qt5::$${CMAKE_MODULE_NAME}) include(\"${CMAKE_CURRENT_LIST_DIR}/Qt5$${CMAKE_MODULE_NAME}Macros.cmake\") !!ENDIF -_qt5_$${CMAKE_MODULE_NAME}_check_file_exists(\"${CMAKE_CURRENT_LIST_DIR}/Qt5$${CMAKE_MODULE_NAME}ConfigVersion.cmake\") - + _qt5_$${CMAKE_MODULE_NAME}_check_file_exists(\"${CMAKE_CURRENT_LIST_DIR}/Qt5$${CMAKE_MODULE_NAME}ConfigVersion.cmake\") endif() -- cgit v1.2.3 From fc1b5eac1a9589301e1804172bfc900f316404b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Mon, 23 Mar 2020 14:48:18 +0100 Subject: QAbstractSocket: Fix usage of deprecated error signal in example Task-number: QTBUG-82605 Change-Id: I83a8777d32302607d48dd670b6c787ac05db88d2 Reviewed-by: Timur Pocheptsov --- examples/network/securesocketclient/sslclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/network/securesocketclient/sslclient.cpp b/examples/network/securesocketclient/sslclient.cpp index 6eb1075996..d6c52a1c66 100644 --- a/examples/network/securesocketclient/sslclient.cpp +++ b/examples/network/securesocketclient/sslclient.cpp @@ -226,7 +226,7 @@ void SslClient::setupSecureSocket() this, &SslClient::socketStateChanged); connect(socket, &QSslSocket::encrypted, this, &SslClient::socketEncrypted); - connect(socket, QOverload::of(&QSslSocket::error), + connect(socket, &QSslSocket::errorOccurred, this, &SslClient::socketError); connect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &SslClient::sslErrors); -- cgit v1.2.3 From 28a0f1ba799e2601eeddcd5a783c72587ea27f60 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 23 Mar 2020 10:58:07 +0100 Subject: Fix use of deprecated ::forcesign Change-Id: I39322bcc0e24d0bfccf43c8700a166ce350208ea Reviewed-by: Friedemann Kleint --- src/corelib/kernel/qcoreapplication_win.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/kernel/qcoreapplication_win.cpp b/src/corelib/kernel/qcoreapplication_win.cpp index 765f129758..824c0535ed 100644 --- a/src/corelib/kernel/qcoreapplication_win.cpp +++ b/src/corelib/kernel/qcoreapplication_win.cpp @@ -772,7 +772,7 @@ QString decodeMSG(const MSG& msg) auto rect = reinterpret_cast(lParam); QTextStream(¶meters) << "DPI: " << HIWORD(wParam) << ',' << LOWORD(wParam) << ' ' << (rect->right - rect->left) << 'x' - << (rect->bottom - rect->top) << forcesign << rect->left << rect->top; + << (rect->bottom - rect->top) << Qt::forcesign << rect->left << rect->top; } break; case WM_IME_NOTIFY: -- cgit v1.2.3 From 456137066113ac2a85cce8b4e7bcd76193675674 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Tue, 24 Mar 2020 10:33:01 +0100 Subject: More qOverload cleanups in qtbase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-82605 Change-Id: I1c3c14ed82911ed5483258c11e76f5dd7744fa12 Reviewed-by: Mårten Nordheim --- examples/network/multistreamclient/client.cpp | 3 +-- src/network/access/qnetworkreply.cpp | 2 +- .../doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp | 2 +- tests/auto/network/access/http2/tst_http2.cpp | 4 +--- tests/auto/network/ssl/qocsp/tst_qocsp.cpp | 3 +-- tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp | 3 +-- 6 files changed, 6 insertions(+), 11 deletions(-) diff --git a/examples/network/multistreamclient/client.cpp b/examples/network/multistreamclient/client.cpp index db6e3ba304..ce84f3df9a 100644 --- a/examples/network/multistreamclient/client.cpp +++ b/examples/network/multistreamclient/client.cpp @@ -126,8 +126,7 @@ Client::Client(QWidget *parent) connect(sctpSocket, &QSctpSocket::connected, this, &Client::connected); connect(sctpSocket, &QSctpSocket::disconnected, this, &Client::disconnected); connect(sctpSocket, &QSctpSocket::channelReadyRead, this, &Client::readDatagram); - connect(sctpSocket, QOverload::of(&QSctpSocket::error), - this, &Client::displayError); + connect(sctpSocket, &QSctpSocket::errorOccurred, this, &Client::displayError); connect(consumers[SctpChannels::Time], &Consumer::writeDatagram, this, &Client::writeDatagram); connect(consumers[SctpChannels::Chat], &Consumer::writeDatagram, this, &Client::writeDatagram); diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp index 6784bc734d..30e4e290ea 100644 --- a/src/network/access/qnetworkreply.cpp +++ b/src/network/access/qnetworkreply.cpp @@ -868,7 +868,7 @@ void QNetworkReply::setRequest(const QNetworkRequest &request) Sets the error condition to be \a errorCode. The human-readable message is set with \a errorString. - Calling setError() does not emit the error(QNetworkReply::NetworkError) + Calling setError() does not emit the errorOccurred(QNetworkReply::NetworkError) signal. \sa error(), errorString() diff --git a/src/network/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp b/src/network/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp index aed74e308e..d317137dad 100644 --- a/src/network/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp +++ b/src/network/doc/snippets/code/src_network_access_qnetworkaccessmanager.cpp @@ -64,7 +64,7 @@ request.setRawHeader("User-Agent", "MyOwnBrowser 1.0"); QNetworkReply *reply = manager->get(request); connect(reply, &QIODevice::readyRead, this, &MyClass::slotReadyRead); -connect(reply, QOverload::of(&QNetworkReply::error), +connect(reply, &QNetworkReply::errorOccurred, this, &MyClass::slotError); connect(reply, &QNetworkReply::sslErrors, this, &MyClass::slotSslErrors); diff --git a/tests/auto/network/access/http2/tst_http2.cpp b/tests/auto/network/access/http2/tst_http2.cpp index 6a07ae78fb..6702f25b16 100644 --- a/tests/auto/network/access/http2/tst_http2.cpp +++ b/tests/auto/network/access/http2/tst_http2.cpp @@ -512,9 +512,7 @@ void tst_Http2::goaway() request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true)); replies[i] = manager->get(request); QCOMPARE(replies[i]->error(), QNetworkReply::NoError); - void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = - &QNetworkReply::errorOccurred; - connect(replies[i], errorSignal, this, &tst_Http2::replyFinishedWithError); + connect(replies[i], &QNetworkReply::errorOccurred, this, &tst_Http2::replyFinishedWithError); // Since we're using self-signed certificates, ignore SSL errors: replies[i]->ignoreSslErrors(); } diff --git a/tests/auto/network/ssl/qocsp/tst_qocsp.cpp b/tests/auto/network/ssl/qocsp/tst_qocsp.cpp index edd1c24547..f877dcab2d 100644 --- a/tests/auto/network/ssl/qocsp/tst_qocsp.cpp +++ b/tests/auto/network/ssl/qocsp/tst_qocsp.cpp @@ -410,7 +410,6 @@ private: static QString certDirPath; - void (QSslSocket::*socketErrorSignal)(QAbstractSocket::SocketError) = &QAbstractSocket::errorOccurred; void (QSslSocket::*tlsErrorsSignal)(const QList &) = &QSslSocket::sslErrors; void (QTestEventLoop::*exitLoopSlot)() = &QTestEventLoop::exitLoop; @@ -764,7 +763,7 @@ void tst_QOcsp::setupOcspClient(QSslSocket &clientSocket, const CertificateChain clientSocket.setSslConfiguration(clientConfig); clientSocket.setPeerVerifyName(name); - connect(&clientSocket, socketErrorSignal, &loop, exitLoopSlot); + connect(&clientSocket, &QAbstractSocket::errorOccurred, &loop, exitLoopSlot); connect(&clientSocket, tlsErrorsSignal, &loop, exitLoopSlot); connect(&clientSocket, &QSslSocket::encrypted, &loop, exitLoopSlot); } diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp index 23495436eb..2d71bdfc26 100644 --- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp @@ -2552,8 +2552,7 @@ void tst_QSslSocket::closeWhileEmittingSocketError() clientSocket.setSslConfiguration(clientConfig); QSignalSpy socketErrorSpy(&clientSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError))); - void (QSslSocket::*errorSignal)(QAbstractSocket::SocketError) = &QSslSocket::errorOccurred; - connect(&clientSocket, errorSignal, &handshake, &BrokenPskHandshake::socketError); + connect(&clientSocket, &QSslSocket::errorOccurred, &handshake, &BrokenPskHandshake::socketError); clientSocket.connectToHostEncrypted(QStringLiteral("127.0.0.1"), handshake.serverPort()); // Make sure we have some data buffered so that close will try to flush: -- cgit v1.2.3 From 5e1e4b9d52987c7c12a935e0ed5ef89f1ca8244c Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 18 Mar 2020 12:22:11 +0100 Subject: Extend tiled optimization in 64-bit painting to 64-bit sources Change-Id: I74b88781d631ee68822cd08f9cb0aca03f7b688e Reviewed-by: Eirik Aavitsland --- src/gui/painting/qdrawhelper.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 39de1baa17..f153557077 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -5139,7 +5139,8 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD yoff += image_height; bool isBpp32 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP32; - if (op.destFetch64 == destFetch64Undefined && image_width <= BufferSize && isBpp32) { + bool isBpp64 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP64; + if (op.destFetch64 == destFetch64Undefined && image_width <= BufferSize && (isBpp32 || isBpp64)) { // If destination isn't blended into the result, we can do the tiling directly on destination pixels. while (count--) { int x = spans->x; @@ -5173,9 +5174,14 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD if (sx >= image_width) sx = 0; } - uint *dest = (uint*)data->rasterBuffer->scanLine(y) + x - image_width; - for (int i = image_width; i < length; ++i) { - dest[i] = dest[i - image_width]; + if (isBpp32) { + uint *dest = reinterpret_cast(data->rasterBuffer->scanLine(y)) + x - image_width; + for (int i = image_width; i < length; ++i) + dest[i] = dest[i - image_width]; + } else { + quint64 *dest = reinterpret_cast(data->rasterBuffer->scanLine(y)) + x - image_width; + for (int i = image_width; i < length; ++i) + dest[i] = dest[i - image_width]; } ++spans; } -- cgit v1.2.3 From d00f28afda79adcc106d1d953563ca03015927ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Tue, 24 Mar 2020 13:00:51 +0100 Subject: Revert "wasm: support emsdk >= 1.39.4" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit bcdf49bcc6c357c9c30708e2e95ce4b8dbeb9f8a. This was a 5.14-only commit which accidentally found its way to 5.15. Change-Id: Iaef6f05fce20ab61d3afb8cb3c359a037e4c0b28 Reviewed-by: Tor Arne Vestbø Reviewed-by: Lorn Potter --- mkspecs/wasm-emscripten/qmake.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/mkspecs/wasm-emscripten/qmake.conf b/mkspecs/wasm-emscripten/qmake.conf index 4a3694e5be..e6a9773482 100644 --- a/mkspecs/wasm-emscripten/qmake.conf +++ b/mkspecs/wasm-emscripten/qmake.conf @@ -37,7 +37,6 @@ EMCC_COMMON_LFLAGS += \ -s NO_EXIT_RUNTIME=0 \ -s ERROR_ON_UNDEFINED_SYMBOLS=1 \ -s EXTRA_EXPORTED_RUNTIME_METHODS=[\"UTF16ToString\",\"stringToUTF16\"] \ - -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 \ --bind # The -s arguments can also be used with release builds, -- cgit v1.2.3 From 1d5eb202b928dc7c73fc0fc4eb480817b339c68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Tue, 10 Mar 2020 23:56:45 +0000 Subject: wasm: Add OpenGL version check QtQuick3D probes for the supported OpenGL level by testing multiple OpenGL versions in descending order. Context creation must fail for versions not supported by WebGL. It does not appear that Emscripten does this for the 3.x minor versions, at least. Add version check which allows OpenGL (ES) 3.0 and 2.0. Change-Id: Ide8745dd79e69af86812a8d6f5d6cc16ecdd099a Reviewed-by: Lorn Potter --- src/plugins/platforms/wasm/qwasmopenglcontext.cpp | 19 +++++++++++++++---- src/plugins/platforms/wasm/qwasmopenglcontext.h | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp index fbf700518e..c122335a57 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp @@ -62,6 +62,16 @@ QWasmOpenGLContext::~QWasmOpenGLContext() } } +bool QWasmOpenGLContext::isOpenGLVersionSupported(QSurfaceFormat format) +{ + // Version check: support WebGL 1 and 2: + // (ES) 2.0 -> WebGL 1.0 + // (ES) 3.0 -> WebGL 2.0 + // [we don't expect that new WebGL versions will be created] + return ((format.majorVersion() == 2 && format.minorVersion() == 0) || + (format.majorVersion() == 3 && format.minorVersion() == 0)); +} + bool QWasmOpenGLContext::maybeCreateEmscriptenContext(QPlatformSurface *surface) { // Native emscripten/WebGL contexts are tied to a single screen/canvas. The first @@ -92,10 +102,8 @@ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(cons attributes.failIfMajorPerformanceCaveat = false; attributes.antialias = true; attributes.enableExtensionsByDefault = true; - - if (format.majorVersion() == 3) { - attributes.majorVersion = 2; - } + attributes.majorVersion = format.majorVersion() - 1; + attributes.minorVersion = format.minorVersion(); // WebGL doesn't allow separate attach buffers to STENCIL_ATTACHMENT and DEPTH_ATTACHMENT // we need both or none @@ -149,6 +157,9 @@ bool QWasmOpenGLContext::isSharing() const bool QWasmOpenGLContext::isValid() const { + if (!(isOpenGLVersionSupported(m_requestedFormat))) + return false; + // Note: we get isValid() calls before we see the surface and can // create a native context, so no context is also a valid state. return !m_context || !emscripten_is_webgl_context_lost(m_context); diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.h b/src/plugins/platforms/wasm/qwasmopenglcontext.h index d27007e8ea..cf84379c36 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.h +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.h @@ -51,6 +51,7 @@ public: QFunctionPointer getProcAddress(const char *procName) override; private: + static bool isOpenGLVersionSupported(QSurfaceFormat format); bool maybeCreateEmscriptenContext(QPlatformSurface *surface); static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const QString &canvasId, QSurfaceFormat format); -- cgit v1.2.3 From b15ed929f3f481914bf96203e691a552f6d9fe00 Mon Sep 17 00:00:00 2001 From: Giulio Camuffo Date: Fri, 20 Mar 2020 14:05:34 +0100 Subject: Fix memory leak When creating a new QColorSpacePrivate it must be reffed otherwise in the destructor the deref() will bring the count to -1 which is true and will not delete the d_ptr. Change-Id: Id569bae22134b56bf6ad37158d7079b495599fd7 Reviewed-by: Allan Sandfeld Jensen (cherry picked from commit 20eabb72de94ddcef3c36b5cad0ce88944d42f8c) --- src/gui/painting/qcolorspace.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp index 7e7bbec870..930e5aec87 100644 --- a/src/gui/painting/qcolorspace.cpp +++ b/src/gui/painting/qcolorspace.cpp @@ -553,6 +553,7 @@ void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunc return; if (!d_ptr) { d_ptr = new QColorSpacePrivate(Primaries::Custom, transferFunction, gamma); + d_ptr->ref.ref(); return; } if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma) @@ -593,6 +594,7 @@ void QColorSpace::setPrimaries(QColorSpace::Primaries primariesId) return; if (!d_ptr) { d_ptr = new QColorSpacePrivate(primariesId, TransferFunction::Custom, 0.0f); + d_ptr->ref.ref(); return; } if (d_ptr->primaries == primariesId) @@ -618,6 +620,7 @@ void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoin return; if (!d_ptr) { d_ptr = new QColorSpacePrivate(primaries, TransferFunction::Custom, 0.0f); + d_ptr->ref.ref(); return; } QColorMatrix toXyz = primaries.toXyzMatrix(); -- cgit v1.2.3 From 02fa39ed22e8ca5889639661b531f1653c6388f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 24 Mar 2020 10:08:49 +0100 Subject: macOS: Flush sublayers via separate IOSurface backingstores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flushing sublayers via QImage copies of the root IOSurface was causing performance regressions due to the constant allocations of new images each frame. We now re-use the QCALayerBackingStore implementation for sublayers, which gives a dynamic swap-chain. We're still paying the CPU cost of the copy from the root backingstore to the layered backingstores, as well as the memory cost, but at least improves the situation. We do not try to be smart and paint directly into the sublayers, as that would leave the root backingstore stale, potentially causing glitches when views are repositioned. Investigating this is left for future work. Fixes: QTBUG-82986 Change-Id: I758a3d8e1e40e2ed4fe6bc590a4a5a988d87a3a7 Reviewed-by: Morten Johan Sørvig Reviewed-by: Tor Arne Vestbø (cherry picked from commit ce2d68ebe1aefeae78ff2fd8ec5ff7e20790ef69) --- src/plugins/platforms/cocoa/qcocoabackingstore.h | 10 ++- src/plugins/platforms/cocoa/qcocoabackingstore.mm | 83 ++++++++++++++++------- 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index b57deacb57..3d9dfd8359 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -47,6 +47,8 @@ #include #include "qiosurfacegraphicsbuffer.h" +#include + QT_BEGIN_NAMESPACE class QCocoaBackingStore : public QRasterBackingStore @@ -71,8 +73,9 @@ private: void redrawRoundedBottomCorners(CGRect) const; }; -class QCALayerBackingStore : public QCocoaBackingStore +class QCALayerBackingStore : public QObject, public QCocoaBackingStore { + Q_OBJECT public: QCALayerBackingStore(QWindow *window); ~QCALayerBackingStore(); @@ -119,6 +122,11 @@ private: QMacNotificationObserver m_backingPropertiesObserver; std::list> m_buffers; + + void flushSubWindow(QWindow *window); + std::unordered_map> m_subWindowBackingstores; + void windowDestroyed(QObject *object); + bool m_clearSurfaceOnPaint = true; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index cb019c3775..2b4c71f279 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -382,7 +382,7 @@ void QCALayerBackingStore::beginPaint(const QRegion ®ion) // Although undocumented, QBackingStore::beginPaint expects the painted region // to be cleared before use if the window has a surface format with an alpha. // Fresh IOSurfaces are already cleared, so we don't need to clear those. - if (!bufferWasRecreated && window()->format().hasAlpha()) { + if (m_clearSurfaceOnPaint && !bufferWasRecreated && window()->format().hasAlpha()) { qCDebug(lcQpaBackingStore) << "Clearing" << region << "before use"; QPainter painter(m_buffers.back()->asImage()); painter.setCompositionMode(QPainter::CompositionMode_Source); @@ -511,9 +511,13 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, if (!prepareForFlush()) return; + if (flushedWindow != window()) { + flushSubWindow(flushedWindow); + return; + } + QMacAutoReleasePool pool; - NSView *backingStoreView = static_cast(window()->handle())->view(); NSView *flushedView = static_cast(flushedWindow->handle())->view(); // If the backingstore is just flushed, without being painted to first, then we may @@ -548,7 +552,7 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, // are committed as part of a display-cycle instead of on the next runloop pass. This // means CA won't try to throttle us if we flush too fast, and we'll coalesce our flush // with other pending view and layer updates. - backingStoreView.window.viewsNeedDisplay = YES; + flushedView.window.viewsNeedDisplay = YES; if (window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer) { // The private API [CALayer reloadValueForKeyPath:@"contents"] would be preferable, @@ -556,28 +560,10 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, flushedView.layer.contents = nil; } - if (flushedView == backingStoreView) { - qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface - << "to" << flushedView.layer << "of" << flushedView; - flushedView.layer.contents = backBufferSurface; - } else { - auto subviewRect = [flushedView convertRect:flushedView.bounds toView:backingStoreView]; - auto scale = flushedView.layer.contentsScale; - subviewRect = CGRectApplyAffineTransform(subviewRect, CGAffineTransformMakeScale(scale, scale)); - - // We make a copy of the image data up front, which means we don't - // need to mark the IOSurface as being in use. FIXME: Investigate - // if there's a cheaper way to get sub-image data to a layer. - m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess); - QImage subImage = m_buffers.back()->asImage()->copy(QRectF::fromCGRect(subviewRect).toRect()); - m_buffers.back()->unlock(); + qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface + << "to" << flushedView.layer << "of" << flushedView; - qCInfo(lcQpaBackingStore) << "Flushing" << subImage - << "to" << flushedView.layer << "of subview" << flushedView; - QCFType cgImage = CGImageCreateCopyWithColorSpace( - QCFType(subImage.toCGImage()), colorSpace()); - flushedView.layer.contents = (__bridge id)static_cast(cgImage); - } + flushedView.layer.contents = backBufferSurface; // Since we may receive multiple flushes before a new frame is started, we do not // swap any buffers just yet. Instead we check in the next beginPaint if the layer's @@ -589,6 +575,53 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, // the window server. } +void QCALayerBackingStore::flushSubWindow(QWindow *subWindow) +{ + qCInfo(lcQpaBackingStore) << "Flushing sub-window" << subWindow + << "via its own backingstore"; + + auto &subWindowBackingStore = m_subWindowBackingstores[subWindow]; + if (!subWindowBackingStore) { + subWindowBackingStore.reset(new QCALayerBackingStore(subWindow)); + QObject::connect(subWindow, &QObject::destroyed, this, &QCALayerBackingStore::windowDestroyed); + subWindowBackingStore->m_clearSurfaceOnPaint = false; + } + + auto subWindowSize = subWindow->size(); + static const auto kNoStaticContents = QRegion(); + subWindowBackingStore->resize(subWindowSize, kNoStaticContents); + + auto subWindowLocalRect = QRect(QPoint(), subWindowSize); + subWindowBackingStore->beginPaint(subWindowLocalRect); + + QPainter painter(subWindowBackingStore->m_buffers.back()->asImage()); + painter.setCompositionMode(QPainter::CompositionMode_Source); + + NSView *backingStoreView = static_cast(window()->handle())->view(); + NSView *flushedView = static_cast(subWindow->handle())->view(); + auto subviewRect = [flushedView convertRect:flushedView.bounds toView:backingStoreView]; + auto scale = flushedView.layer.contentsScale; + subviewRect = CGRectApplyAffineTransform(subviewRect, CGAffineTransformMakeScale(scale, scale)); + + m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess); + const QImage *backingStoreImage = m_buffers.back()->asImage(); + painter.drawImage(subWindowLocalRect, *backingStoreImage, QRectF::fromCGRect(subviewRect)); + m_buffers.back()->unlock(); + + painter.end(); + subWindowBackingStore->endPaint(); + subWindowBackingStore->flush(subWindow, subWindowLocalRect, QPoint()); + + qCInfo(lcQpaBackingStore) << "Done flushing sub-window" << subWindow; +} + +void QCALayerBackingStore::windowDestroyed(QObject *object) +{ + auto *window = static_cast(object); + qCInfo(lcQpaBackingStore) << "Removing backingstore for sub-window" << window; + m_subWindowBackingstores.erase(window); +} + #ifndef QT_NO_OPENGL void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground) @@ -722,4 +755,6 @@ QImage *QCALayerBackingStore::GraphicsBuffer::asImage() return &m_image; } +#include "moc_qcocoabackingstore.cpp" + QT_END_NAMESPACE -- cgit v1.2.3 From 46ebd11e66f6f3180043a7258b46a1aff79a69f8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 25 Mar 2020 11:20:28 +0100 Subject: Fix deprecation of QComboBox:::currentIndexChanged(const QString&) Don't introduce another overload with two parameters. Users want a simple signal to connect to, not another overload. Deprecate the currentIndexChanged(QString) overload, usage of that can/should get replaced by currentTextChanged(). This partially reverts commit 11dc7b35c8c3957bd19087a5e3ae7cfc4f1a3343. Change-Id: I5e7d16413f3d62b1a5a7a197f510af2c45cdfa55 Reviewed-by: Vitaly Fanaskov --- src/widgets/widgets/qcombobox.cpp | 37 ++++++++-------------- src/widgets/widgets/qcombobox.h | 7 ++-- .../widgets/widgets/qcombobox/tst_qcombobox.cpp | 20 +++++++----- 3 files changed, 29 insertions(+), 35 deletions(-) diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index df8e7b4a3f..84ba647975 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -904,7 +904,8 @@ QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const This signal is sent when the user chooses an item in the combobox. The item's \a index is passed. Note that this signal is sent even when the choice is not changed. If you need to know when the - choice actually changes, use signal currentIndexChanged(). + choice actually changes, use signal currentIndexChanged() or + currentTextChanged(). */ @@ -914,7 +915,8 @@ QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const This signal is sent when the user chooses an item in the combobox. The item's \a text is passed. Note that this signal is sent even when the choice is not changed. If you need to know when the - choice actually changes, use signal currentIndexChanged(). + choice actually changes, use signal currentIndexChanged() or + currentTextChanged(). \obsolete Use QComboBox::textActivated() instead */ @@ -925,7 +927,8 @@ QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const This signal is sent when the user chooses an item in the combobox. The item's \a text is passed. Note that this signal is sent even when the choice is not changed. If you need to know when the - choice actually changes, use signal currentIndexChanged(). + choice actually changes, use signal currentIndexChanged() or + currentTextChanged(). */ /*! @@ -959,8 +962,6 @@ QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const changes either through user interaction or programmatically. The item's \a index is passed or -1 if the combobox becomes empty or the currentIndex was reset. - - \obsolete Use currentIndexChanged(int index, const QString &text) instead */ /*! @@ -971,17 +972,8 @@ QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const changes either through user interaction or programmatically. The item's \a text is passed. - \obsolete Use currentIndexChanged(int index, const QString &text) instead -*/ - -/*! - \fn void QComboBox::currentIndexChanged(int index, const QString &text) - \since 5.15 - - This signal is sent whenever the currentIndex in the combobox - changes either through user interaction or programmatically. The - item's \a index is passed or -1 if the combobox becomes empty or - the currentIndex was reset. The item's \a text is also passed. + \obsolete Use currentIndexChanged(int) and get the text from + the itemText(int) method. */ /*! @@ -1013,7 +1005,6 @@ QComboBox::QComboBox(QComboBoxPrivate &dd, QWidget *parent) d->init(); } - /*! \class QComboBox \brief The QComboBox widget is a combined button and popup list. @@ -1036,9 +1027,10 @@ QComboBox::QComboBox(QComboBoxPrivate &dd, QWidget *parent) to clear the displayed string without changing the combobox's contents. - There are two signals emitted if the current item of a combobox - changes, currentIndexChanged() and activated(). - currentIndexChanged() is always emitted regardless if the change + There are three signals emitted if the current item of a combobox + changes, currentIndexChanged(), currentTextChanged() and activated(). + currentIndexChanged() and currentTextChanged() are always emitted + regardless if the change was done programmatically or by user interaction, while activated() is only emitted when the change is caused by user interaction. The highlighted() signal is emitted when the user @@ -1466,14 +1458,13 @@ void QComboBoxPrivate::_q_emitCurrentIndexChanged(const QModelIndex &index) { Q_Q(QComboBox); const QString text = itemText(index); -#if QT_DEPRECATED_SINCE(5, 15) + emit q->currentIndexChanged(index.row()); +#if QT_DEPRECATED_SINCE(5, 13) QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED - emit q->currentIndexChanged(index.row()); emit q->currentIndexChanged(text); QT_WARNING_POP #endif - emit q->currentIndexChanged(index.row(), text); // signal lineEdit.textChanged already connected to signal currentTextChanged, so don't emit double here if (!lineEdit) emit q->currentTextChanged(text); diff --git a/src/widgets/widgets/qcombobox.h b/src/widgets/widgets/qcombobox.h index e0b213bc89..4d7ca4b635 100644 --- a/src/widgets/widgets/qcombobox.h +++ b/src/widgets/widgets/qcombobox.h @@ -233,13 +233,12 @@ Q_SIGNALS: void textActivated(const QString &); void highlighted(int index); void textHighlighted(const QString &); -#if QT_DEPRECATED_SINCE(5, 15) - QT_DEPRECATED_VERSION_X_5_15("Use currentIndexChanged(int, const QString &) instead") void currentIndexChanged(int index); - QT_DEPRECATED_VERSION_X_5_15("Use currentIndexChanged(int, const QString &) instead") +#if QT_DEPRECATED_SINCE(5, 15) + QT_DEPRECATED_VERSION_X_5_15( + "Use currentIndexChanged(int) instead, and get the text using itemText(index)") void currentIndexChanged(const QString &); #endif - void currentIndexChanged(int index, const QString &text); void currentTextChanged(const QString &); #if QT_DEPRECATED_SINCE(5, 15) QT_DEPRECATED_VERSION_X(5, 15, "Use textActivated() instead") diff --git a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp index c5be8a3f16..c934f8e27b 100644 --- a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +++ b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp @@ -1208,7 +1208,8 @@ void tst_QComboBox::currentIndex() QVERIFY(testWidget->currentText().isEmpty()); // spy on currentIndexChanged - QSignalSpy indexChangedSpy(testWidget, SIGNAL(currentIndexChanged(int, QString))); + QSignalSpy indexChangedInt(testWidget, SIGNAL(currentIndexChanged(int))); + QSignalSpy indexChangedString(testWidget, SIGNAL(currentIndexChanged(QString))); // stuff items into it foreach(QString text, initialItems) { @@ -1232,12 +1233,16 @@ void tst_QComboBox::currentIndex() QCOMPARE(testWidget->currentText(), expectedCurrentText); // check that signal count is correct - QCOMPARE(indexChangedSpy.count(), expectedSignalCount); + QCOMPARE(indexChangedInt.count(), expectedSignalCount); + QCOMPARE(indexChangedString.count(), expectedSignalCount); // compare with last sent signal values - if (indexChangedSpy.count()) - QCOMPARE(indexChangedSpy.at(indexChangedSpy.count() - 1).at(0).toInt(), - testWidget->currentIndex()); + if (indexChangedInt.count()) + QCOMPARE(indexChangedInt.at(indexChangedInt.count() - 1).at(0).toInt(), + testWidget->currentIndex()); + if (indexChangedString.count()) + QCOMPARE(indexChangedString.at(indexChangedString.count() - 1).at(0).toString(), + testWidget->currentText()); if (edit) { testWidget->setCurrentIndex(-1); @@ -2336,8 +2341,7 @@ public: { QStringList list; list << "one" << "two"; - connect(this, SIGNAL(currentIndexChanged(int, QString)), - this, SLOT(onCurrentIndexChanged(int))); + connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int))); addItems(list); } public slots: @@ -2763,7 +2767,7 @@ void tst_QComboBox::resetModel() }; QComboBox cb; StringListModel model({"1", "2"}); - QSignalSpy spy(&cb, QOverload::of(&QComboBox::currentIndexChanged)); + QSignalSpy spy(&cb, QOverload::of(&QComboBox::currentIndexChanged)); QCOMPARE(spy.count(), 0); QCOMPARE(cb.currentIndex(), -1); //no selection -- cgit v1.2.3 From cc333f5faf2fa892ca0e321915263cab5d793191 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 28 Jan 2020 11:46:16 +0100 Subject: Diaglib: Fix build Use range-based for and fix deprecation warnings. Change-Id: I54152b2598e9e4a7a3cc9db9b7072bbabcef7fcf Reviewed-by: Shawn Rutledge --- tests/manual/diaglib/debugproxystyle.cpp | 2 +- tests/manual/diaglib/nativewindowdump_win.cpp | 10 +++++----- tests/manual/diaglib/qwidgetdump.cpp | 10 +++++----- tests/manual/diaglib/qwindowdump.cpp | 10 +++++----- tests/manual/diaglib/textdump.cpp | 6 +++--- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/manual/diaglib/debugproxystyle.cpp b/tests/manual/diaglib/debugproxystyle.cpp index ed35af5962..73e9e7ce54 100644 --- a/tests/manual/diaglib/debugproxystyle.cpp +++ b/tests/manual/diaglib/debugproxystyle.cpp @@ -69,7 +69,7 @@ QDebug operator<<(QDebug debug, const QStyleOption *option) debug << "QStyleOption("; } debug << "rect=" << option->rect.width() << 'x' << option->rect.height() - << forcesign << option->rect.x() << option->rect.y() << noforcesign; + << Qt::forcesign << option->rect.x() << option->rect.y() << Qt::noforcesign; if (option->state != QStyle::State_None) debug << ", state=" << option->state; #if QT_VERSION >= 0x050000 diff --git a/tests/manual/diaglib/nativewindowdump_win.cpp b/tests/manual/diaglib/nativewindowdump_win.cpp index d91e673d1c..256142462f 100644 --- a/tests/manual/diaglib/nativewindowdump_win.cpp +++ b/tests/manual/diaglib/nativewindowdump_win.cpp @@ -69,7 +69,7 @@ static QTextStream &operator<<(QTextStream &str, const QSize &s) static QTextStream &operator<<(QTextStream &str, const QRect &rect) { - str << rect.size() << forcesign << rect.x() << rect.y() << noforcesign; + str << rect.size() << Qt::forcesign << rect.x() << rect.y() << Qt::noforcesign; return str; } @@ -110,7 +110,7 @@ static bool isTopLevel(HWND hwnd) static void formatNativeWindow(HWND hwnd, QTextStream &str) { - str << hex << showbase << quintptr(hwnd) << noshowbase << dec; + str << Qt::hex << Qt::showbase << quintptr(hwnd) << Qt::noshowbase << Qt::dec; const bool topLevel = isTopLevel(hwnd); if (topLevel) @@ -136,7 +136,7 @@ static void formatNativeWindow(HWND hwnd, QTextStream &str) if (GetClassName(hwnd, buf, sizeof(buf)/sizeof(buf[0]))) str << '"' << QString::fromWCharArray(buf) << '"'; - str << hex << showbase; + str << Qt::hex << Qt::showbase; if (const LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE)) { str << " style=" << style; debugWinStyle(str, style, WS_OVERLAPPED) @@ -208,7 +208,7 @@ static void formatNativeWindow(HWND hwnd, QTextStream &str) if (const ULONG_PTR wndProc = GetClassLongPtr(hwnd, GCLP_WNDPROC)) str << " wndProc=" << wndProc; - str << noshowbase << dec; + str << Qt::noshowbase << Qt::dec; if (GetWindowModuleFileName(hwnd, buf, sizeof(buf)/sizeof(buf[0]))) str << " module=\"" << QString::fromWCharArray(buf) << '"'; @@ -258,7 +258,7 @@ static void dumpNativeWindows(const WIdVector& wins) DumpContext dc; QString s; dc.stream = QSharedPointer(new QTextStream(&s)); - foreach (WId win, wins) + for (WId win : wins) dumpNativeWindowRecursion(reinterpret_cast(win), &dc); #if QT_VERSION >= 0x050400 qDebug().noquote() << s; diff --git a/tests/manual/diaglib/qwidgetdump.cpp b/tests/manual/diaglib/qwidgetdump.cpp index 5a2966021b..9e175cd3cf 100644 --- a/tests/manual/diaglib/qwidgetdump.cpp +++ b/tests/manual/diaglib/qwidgetdump.cpp @@ -86,14 +86,14 @@ static void dumpWidgetRecursion(QTextStream &str, const QWidget *w, formatWidgetClass(str, w); str << ' ' << (w->isVisible() ? "[visible] " : "[hidden] "); if (const WId nativeWinId = w->internalWinId()) - str << "[native: " << hex << showbase << nativeWinId << dec << noshowbase << "] "; + str << "[native: " << Qt::hex << Qt::showbase << nativeWinId << Qt::dec << Qt::noshowbase << "] "; if (w->isWindow()) str << "[top] "; str << (w->testAttribute(Qt::WA_Mapped) ? "[mapped] " : "[not mapped] "); if (w->testAttribute(Qt::WA_DontCreateNativeAncestors)) str << "[NoNativeAncestors] "; if (const int states = w->windowState()) - str << "windowState=" << hex << showbase << states << dec << noshowbase << ' '; + str << "windowState=" << Qt::hex << Qt::showbase << states << Qt::dec << Qt::noshowbase << ' '; formatRect(str, w->geometry()); if (w->isWindow()) { str << ' ' << w->logicalDpiX() << "DPI"; @@ -135,7 +135,7 @@ static void dumpWidgetRecursion(QTextStream &str, const QWidget *w, str << '\n'; } #endif // Qt 5 - foreach (const QObject *co, w->children()) { + for (const QObject *co : w->children()) { if (co->isWidgetType()) dumpWidgetRecursion(str, static_cast(co), options, depth + 1); } @@ -151,11 +151,11 @@ void dumpAllWidgets(FormatWindowOptions options, const QWidget *root) topLevels.append(const_cast(root)); else topLevels = QApplication::topLevelWidgets(); - foreach (QWidget *tw, topLevels) + for (QWidget *tw : qAsConst(topLevels)) dumpWidgetRecursion(str, tw, options); #if QT_VERSION >= 0x050400 { - foreach (const QString &line, d.split(QLatin1Char('\n'))) + for (const QString &line : d.split(QLatin1Char('\n'))) qDebug().noquote() << line; } #else diff --git a/tests/manual/diaglib/qwindowdump.cpp b/tests/manual/diaglib/qwindowdump.cpp index 381b683359..83e29fc183 100644 --- a/tests/manual/diaglib/qwindowdump.cpp +++ b/tests/manual/diaglib/qwindowdump.cpp @@ -62,7 +62,7 @@ void formatObject(QTextStream &str, const QObject *o) void formatRect(QTextStream &str, const QRect &geom) { str << geom.width() << 'x' << geom.height() - << forcesign << geom.x() << geom.y() << noforcesign; + << Qt::forcesign << geom.x() << geom.y() << Qt::noforcesign; } #define debugType(s, type, typeConstant) \ @@ -75,7 +75,7 @@ if (flags & flagConstant) \ void formatWindowFlags(QTextStream &str, Qt::WindowFlags flags) { - str << showbase << hex << unsigned(flags) << dec << noshowbase; + str << Qt::showbase << Qt::hex << unsigned(flags) << Qt::dec << Qt::noshowbase; const Qt::WindowFlags windowType = flags & Qt::WindowType_Mask; debugFlag(str, flags, Qt::Window) debugType(str, windowType, Qt::Dialog) @@ -123,7 +123,7 @@ void formatWindow(QTextStream &str, const QWindow *w, FormatWindowOptions option formatObject(str, w); str << ' ' << (w->isVisible() ? "[visible] " : "[hidden] "); if (const WId nativeWinId = pw ? pw->winId() : WId(0)) - str << "[native: " << hex << showbase << nativeWinId << dec << noshowbase << "] "; + str << "[native: " << Qt::hex << Qt::showbase << nativeWinId << Qt::dec << Qt::noshowbase << "] "; if (w->isTopLevel()) str << "[top] "; if (w->isExposed()) @@ -162,7 +162,7 @@ static void dumpWindowRecursion(QTextStream &str, const QWindow *w, { indentStream(str, 2 * depth); formatWindow(str, w, options); - foreach (const QObject *co, w->children()) { + for (const QObject *co : w->children()) { if (co->isWindowType()) dumpWindowRecursion(str, static_cast(co), options, depth + 1); } @@ -173,7 +173,7 @@ void dumpAllWindows(FormatWindowOptions options) QString d; QTextStream str(&d); str << "### QWindows:\n"; - foreach (QWindow *w, QGuiApplication::topLevelWindows()) + for (QWindow *w : QGuiApplication::topLevelWindows()) dumpWindowRecursion(str, w, options); #if QT_VERSION >= 0x050400 qDebug().noquote() << d; diff --git a/tests/manual/diaglib/textdump.cpp b/tests/manual/diaglib/textdump.cpp index 383ec4edb0..61fb494785 100644 --- a/tests/manual/diaglib/textdump.cpp +++ b/tests/manual/diaglib/textdump.cpp @@ -408,8 +408,8 @@ struct FormattingContext static void formatCharacter(QTextStream &str, const QChar &qc, FormattingContext &context) { const ushort unicode = qc.unicode(); - str << "U+" << qSetFieldWidth(4) << qSetPadChar('0') << uppercasedigits << hex << unicode - << dec << qSetFieldWidth(0) << ' '; + str << "U+" << qSetFieldWidth(4) << qSetPadChar('0') << Qt::uppercasedigits + << Qt::hex << unicode << Qt::dec << qSetFieldWidth(0) << ' '; const EnumLookup *specialChar = enumLookup(unicode, specialCharactersEnumLookup, sizeof(specialCharactersEnumLookup) / sizeof(EnumLookup)); if (specialChar) @@ -477,7 +477,7 @@ QString dumpTextAsCode(const QString &text) { QString result; QTextStream str(&result); - str << " QString result;\n" << hex << showbase; + str << " QString result;\n" << Qt::hex << Qt::showbase; for (QChar c : text) str << " result += QChar(" << c.unicode() << ");\n"; str << '\n'; -- cgit v1.2.3 From e63d2272895ea5178f0704546dc5b5deed4b1b39 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Tue, 26 Nov 2019 16:06:37 +0100 Subject: CMake: Warn about using internal macros and functions These functions and macros were never documented. Let's warn about using them for now, with the option of removing / renaming them in Qt 6. Change-Id: Ia595aa35b73e54534e535d2946581651af8023f2 Reviewed-by: Alexandru Croitor --- src/corelib/Qt5CoreMacros.cmake | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/corelib/Qt5CoreMacros.cmake b/src/corelib/Qt5CoreMacros.cmake index f666c67ed9..88350cb68b 100644 --- a/src/corelib/Qt5CoreMacros.cmake +++ b/src/corelib/Qt5CoreMacros.cmake @@ -1,5 +1,6 @@ #============================================================================= # Copyright 2005-2011 Kitware, Inc. +# Copyright (C) 2020 The Qt Company Ltd. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -38,8 +39,17 @@ include(CMakeParseArguments) +function(_qt5_warn_deprecated command_name) + if(NOT DEFINED _QT5_INTERNAL_SCOPE) + message(AUTHOR_WARNING + "${command_name} is not part of the official API, and might be removed in Qt 6.") + endif() +endfunction() + # macro used to create the names of output files preserving relative dirs macro(qt5_make_output_file infile prefix ext outfile ) + _qt5_warn_deprecated("qt5_make_output_file") + string(LENGTH ${CMAKE_CURRENT_BINARY_DIR} _binlength) string(LENGTH ${infile} _infileLength) set(_checkinfile ${CMAKE_CURRENT_SOURCE_DIR}) @@ -73,6 +83,8 @@ endmacro() macro(qt5_get_moc_flags _moc_flags) + _qt5_warn_deprecated("qt5_get_moc_flags") + set(${_moc_flags}) get_directory_property(_inc_DIRS INCLUDE_DIRECTORIES) @@ -105,6 +117,8 @@ endmacro() # helper macro to set up a moc rule function(qt5_create_moc_command infile outfile moc_flags moc_options moc_target moc_depends) + _qt5_warn_deprecated("qt5_create_moc_command") + # Pass the parameters in a file. Set the working directory to # be that containing the parameters file and reference it by # just the file name. This is necessary because the moc tool on @@ -151,6 +165,8 @@ endfunction() function(qt5_generate_moc infile outfile ) + set(_QT5_INTERNAL_SCOPE ON) + # get include dirs and flags qt5_get_moc_flags(moc_flags) get_filename_component(abs_infile ${infile} ABSOLUTE) @@ -177,7 +193,9 @@ endif() # qt5_wrap_cpp(outfiles inputfile ... ) -function(qt5_wrap_cpp outfiles ) +function(qt5_wrap_cpp outfiles) + set(_QT5_INTERNAL_SCOPE ON) + # get include dirs qt5_get_moc_flags(moc_flags) @@ -250,7 +268,8 @@ endfunction() # qt5_add_binary_resources(target inputfiles ... ) -function(qt5_add_binary_resources target ) +function(qt5_add_binary_resources target) + set(_QT5_INTERNAL_SCOPE ON) set(options) set(oneValueArgs DESTINATION) @@ -296,7 +315,8 @@ endif() # qt5_add_resources(outfiles inputfile ... ) -function(qt5_add_resources outfiles ) +function(qt5_add_resources outfiles) + set(_QT5_INTERNAL_SCOPE ON) set(options) set(oneValueArgs) @@ -345,7 +365,9 @@ endif() # qt5_add_big_resources(outfiles inputfile ... ) -function(qt5_add_big_resources outfiles ) +function(qt5_add_big_resources outfiles) + set(_QT5_INTERNAL_SCOPE ON) + if (CMAKE_VERSION VERSION_LESS 3.9) message(FATAL_ERROR, "qt5_add_big_resources requires CMake 3.9 or newer") endif() @@ -406,15 +428,7 @@ endif() set(_Qt5_COMPONENT_PATH "${CMAKE_CURRENT_LIST_DIR}/..") macro(qt5_use_modules _target _link_type) - if(CMAKE_WARN_DEPRECATED) - set(messageType WARNING) - endif() - if(CMAKE_ERROR_DEPRECATED) - set(messageType FATAL_ERROR) - endif() - if(messageType) - message(${messageType} "The qt5_use_modules macro is obsolete. Use target_link_libraries with IMPORTED targets instead.") - endif() + _qt5_warn_deprecated("qt5_use_modules") if (NOT TARGET ${_target}) message(FATAL_ERROR "The first argument to qt5_use_modules must be an existing target.") -- cgit v1.2.3 From b1e3f33a28aad1d9386ff2ef569db90341a8d68a Mon Sep 17 00:00:00 2001 From: Michal Klocek Date: Thu, 12 Mar 2020 17:07:08 +0100 Subject: Call post routines from ~QGuiApplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently depending if user uses QApplication or QGuiApplication we end up in different behavior when running post routines. For example QApplication destructor calls post routines before stopping event dispatcher, In case of QGuiApplication post routines are called from QCoreApplication destructor, so no more event dispatcher. This behavior is not consistent and creates troubles when releasing resources of web engine. Attached test will hang on windows with QGuiApplication, however works fine with QApplication. Task-number: QTBUG-79864 Change-Id: Ice05e66a467feaf3ad6addfbc14973649da8065e Reviewed-by: Tor Arne Vestbø --- src/gui/kernel/qguiapplication.cpp | 3 + tests/auto/gui/kernel/kernel.pro | 3 +- .../gui/kernel/qaddpostroutine/qaddpostroutine.pro | 7 +++ .../kernel/qaddpostroutine/tst_qaddpostroutine.cpp | 66 ++++++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 tests/auto/gui/kernel/qaddpostroutine/qaddpostroutine.pro create mode 100644 tests/auto/gui/kernel/qaddpostroutine/tst_qaddpostroutine.cpp diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 469563c415..b9e8db8789 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -134,6 +134,7 @@ QT_BEGIN_NAMESPACE return __VA_ARGS__; \ } +Q_CORE_EXPORT void qt_call_post_routines(); Q_GUI_EXPORT bool qt_is_gui_used = true; Qt::MouseButtons QGuiApplicationPrivate::mouse_buttons = Qt::NoButton; @@ -678,6 +679,8 @@ QGuiApplication::~QGuiApplication() { Q_D(QGuiApplication); + qt_call_post_routines(); + d->eventDispatcher->closingDown(); d->eventDispatcher = nullptr; diff --git a/tests/auto/gui/kernel/kernel.pro b/tests/auto/gui/kernel/kernel.pro index 42135dae24..3187ea3720 100644 --- a/tests/auto/gui/kernel/kernel.pro +++ b/tests/auto/gui/kernel/kernel.pro @@ -25,7 +25,8 @@ SUBDIRS=\ qguiapplication \ qpixelformat \ qopenglwindow \ - qrasterwindow + qrasterwindow \ + qaddpostroutine win32:!winrt:qtHaveModule(network): SUBDIRS += noqteventloop diff --git a/tests/auto/gui/kernel/qaddpostroutine/qaddpostroutine.pro b/tests/auto/gui/kernel/qaddpostroutine/qaddpostroutine.pro new file mode 100644 index 0000000000..e67720b5d5 --- /dev/null +++ b/tests/auto/gui/kernel/qaddpostroutine/qaddpostroutine.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +TARGET = tst_qaddpostroutine + +QT += testlib + +SOURCES += tst_qaddpostroutine.cpp + diff --git a/tests/auto/gui/kernel/qaddpostroutine/tst_qaddpostroutine.cpp b/tests/auto/gui/kernel/qaddpostroutine/tst_qaddpostroutine.cpp new file mode 100644 index 0000000000..500543d2e1 --- /dev/null +++ b/tests/auto/gui/kernel/qaddpostroutine/tst_qaddpostroutine.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +static bool done = false; + +static void cleanup() +{ + done = true; + QEventLoop loop; + QTimer::singleShot(100,&loop, &QEventLoop::quit); + loop.exec(); +} + +struct tst_qAddPostRoutine : public QObject +{ +public: + tst_qAddPostRoutine(); + ~tst_qAddPostRoutine(); +}; + +tst_qAddPostRoutine::tst_qAddPostRoutine() +{ + qAddPostRoutine(cleanup); +} + +tst_qAddPostRoutine::~tst_qAddPostRoutine() +{ + Q_ASSERT(done); +} +int main(int argc, char *argv[]) +{ + tst_qAddPostRoutine tc; + QGuiApplication app(argc, argv); + app.setAttribute(Qt::AA_Use96Dpi, true); + QTEST_SET_MAIN_SOURCE_PATH + return QTest::qExec(&tc, argc, argv); +} -- cgit v1.2.3 From f7f9977a6f7e6eb8ab70b14737fa66f969360b5a Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 24 Mar 2020 16:09:28 +0100 Subject: Detect Visual C++ 2019 mode in clang_cl / intel_icl Change-Id: Icdca1551a56e894f6266b33ac059bbbfa3b18453 Reviewed-by: Thiago Macieira --- mkspecs/common/msvc-based-version.conf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mkspecs/common/msvc-based-version.conf b/mkspecs/common/msvc-based-version.conf index 38aecbaf59..b53e3b61aa 100644 --- a/mkspecs/common/msvc-based-version.conf +++ b/mkspecs/common/msvc-based-version.conf @@ -29,4 +29,9 @@ greaterThan(QMAKE_MSC_VER, 1910) { COMPAT_MKSPEC = } +greaterThan(QMAKE_MSC_VER, 1919) { + # Visual Studio 2019 (16.0) / Visual C++ 19.20 and up + MSVC_VER = 16.0 +} + !isEmpty(COMPAT_MKSPEC):!$$COMPAT_MKSPEC: CONFIG += $$COMPAT_MKSPEC -- cgit v1.2.3 From 2b9137426182fb0e0c3ef37cfa8dec0762ad52b4 Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Wed, 25 Mar 2020 10:04:55 +0100 Subject: Fuzzing: Add fuzz target for QRegularExpression::optimize Change-Id: I693af83caed60cdffc83af368a15567d72524844 Reviewed-by: Albert Astals Cid Reviewed-by: Shawn Rutledge --- .../text/qregularexpression/optimize/main.cpp | 35 ++++++++++++++++++++++ .../text/qregularexpression/optimize/optimize.pro | 8 +++++ 2 files changed, 43 insertions(+) create mode 100644 tests/libfuzzer/corelib/text/qregularexpression/optimize/main.cpp create mode 100644 tests/libfuzzer/corelib/text/qregularexpression/optimize/optimize.pro diff --git a/tests/libfuzzer/corelib/text/qregularexpression/optimize/main.cpp b/tests/libfuzzer/corelib/text/qregularexpression/optimize/main.cpp new file mode 100644 index 0000000000..30a714c250 --- /dev/null +++ b/tests/libfuzzer/corelib/text/qregularexpression/optimize/main.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) { + QRegularExpression qre(QByteArray::fromRawData(Data, Size)); + qre.optimize(); + return 0; +} diff --git a/tests/libfuzzer/corelib/text/qregularexpression/optimize/optimize.pro b/tests/libfuzzer/corelib/text/qregularexpression/optimize/optimize.pro new file mode 100644 index 0000000000..196aca42eb --- /dev/null +++ b/tests/libfuzzer/corelib/text/qregularexpression/optimize/optimize.pro @@ -0,0 +1,8 @@ +QT -= gui +SOURCES += main.cpp +FUZZ_ENGINE = $$(LIB_FUZZING_ENGINE) +isEmpty(FUZZ_ENGINE) { + QMAKE_LFLAGS += -fsanitize=fuzzer +} else { + LIBS += $$FUZZ_ENGINE +} -- cgit v1.2.3 From 4605583fec7b101057f72b0c8b967fcfac07e12d Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 21 Feb 2020 12:41:19 -0800 Subject: forkfd: introduce forkfd_wait4() that takes options "wait4" because it looks like the wait4() BSD function, which has the signature: pid_t wait4(pid_t pid, int *wstatus, int options, struct rusage *rusage); And because ours also has 4 parameters. Having options is important anyway. I might want to add some more later, but we can't really support them with the fall back implementation (in fact, we don't honor WNOHANG in the fall back implementation either). Change-Id: I4e559af2a9a1455ab770fffd15f5858bb357e15b Reviewed-by: Edward Welbourne Reviewed-by: Oswald Buddenhagen --- src/3rdparty/forkfd/forkfd.c | 22 ++++++++++++++++------ src/3rdparty/forkfd/forkfd.h | 9 ++++++++- src/3rdparty/forkfd/forkfd_freebsd.c | 22 +++++++++++++++------- src/3rdparty/forkfd/forkfd_linux.c | 25 +++++++++++++++++-------- 4 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c index 31189fa2cd..795aa9dd68 100644 --- a/src/3rdparty/forkfd/forkfd.c +++ b/src/3rdparty/forkfd/forkfd.c @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 Intel Corporation. +** Copyright (C) 2020 Intel Corporation. ** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com ** ** Permission is hereby granted, free of charge, to any person obtaining a copy @@ -94,7 +94,7 @@ static int system_has_forkfd(void); static int system_forkfd(int flags, pid_t *ppid, int *system); -static int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage); +static int system_forkfd_wait(int ffd, struct forkfd_info *info, int ffdwoptions, struct rusage *rusage); #define CHILDREN_IN_SMALL_ARRAY 16 #define CHILDREN_IN_BIG_ARRAY 256 @@ -225,6 +225,16 @@ static void convertStatusToForkfdInfo(int status, struct forkfd_info *info) } } +static int convertForkfdWaitFlagsToWaitFlags(int ffdoptions) +{ + int woptions = WEXITED; + if (ffdoptions & FFDW_NOWAIT) + woptions |= WNOWAIT; + if (ffdoptions & FFDW_NOHANG) + woptions |= WNOHANG; + return woptions; +} + static int tryReaping(pid_t pid, struct pipe_payload *payload) { /* reap the child */ @@ -800,14 +810,13 @@ out: } #endif // _POSIX_SPAWN && !FORKFD_NO_SPAWNFD - -int forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage) +int forkfd_wait4(int ffd, struct forkfd_info *info, int options, struct rusage *rusage) { struct pipe_payload payload; int ret; if (system_has_forkfd()) - return system_forkfd_wait(ffd, info, rusage); + return system_forkfd_wait(ffd, info, options, rusage); ret = read(ffd, &payload, sizeof(payload)); if (ret == -1) @@ -846,10 +855,11 @@ int system_forkfd(int flags, pid_t *ppid, int *system) return -1; } -int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage) +int system_forkfd_wait(int ffd, struct forkfd_info *info, int options, struct rusage *rusage) { (void)ffd; (void)info; + (void)options; (void)rusage; return -1; } diff --git a/src/3rdparty/forkfd/forkfd.h b/src/3rdparty/forkfd/forkfd.h index fe70371719..205928cc2b 100644 --- a/src/3rdparty/forkfd/forkfd.h +++ b/src/3rdparty/forkfd/forkfd.h @@ -44,13 +44,20 @@ extern "C" { #define FFD_CHILD_PROCESS (-2) +#define FFDW_NOHANG 1 /* WNOHANG */ +#define FFDW_NOWAIT 2 /* WNOWAIT */ + struct forkfd_info { int32_t code; int32_t status; }; int forkfd(int flags, pid_t *ppid); -int forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage); +int forkfd_wait4(int ffd, struct forkfd_info *info, int options, struct rusage *rusage); +static inline int forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage) +{ + return forkfd_wait4(ffd, info, 0, rusage); +} int forkfd_close(int ffd); #if _POSIX_SPAWN > 0 diff --git a/src/3rdparty/forkfd/forkfd_freebsd.c b/src/3rdparty/forkfd/forkfd_freebsd.c index 77ce3fcfad..c4ca796ccd 100644 --- a/src/3rdparty/forkfd/forkfd_freebsd.c +++ b/src/3rdparty/forkfd/forkfd_freebsd.c @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 Intel Corporation. +** Copyright (C) 2020 Intel Corporation. ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal @@ -29,6 +29,10 @@ #include "forkfd_atomic.h" +// in forkfd.c +static int convertForkfdWaitFlagsToWaitFlags(int ffdoptions); +static void convertStatusToForkfdInfo(int status, struct forkfd_info *info); + #if __FreeBSD__ >= 10 /* On FreeBSD 10, PROCDESC was enabled by default. On v11, it's not an option * anymore and can't be disabled. */ @@ -81,19 +85,23 @@ int system_forkfd(int flags, pid_t *ppid, int *system) return ret; } -int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage) +int system_forkfd_wait(int ffd, struct forkfd_info *info, int ffdoptions, struct rusage *rusage) { pid_t pid; int status; - int options = WEXITED; + int options = convertForkfdWaitFlagsToWaitFlags(ffdoptions); int ret = pdgetpid(ffd, &pid); if (ret == -1) return ret; - ret = fcntl(ffd, F_GETFL); - if (ret == -1) - return ret; - options |= (ret & O_NONBLOCK) ? WNOHANG : 0; + + if ((options & WNOHANG) == 0) { + /* check if the file descriptor is non-blocking */ + ret = fcntl(ffd, F_GETFL); + if (ret == -1) + return ret; + options |= (ret & O_NONBLOCK) ? WNOHANG : 0; + } ret = wait4(pid, &status, options, rusage); if (ret != -1 && info) convertStatusToForkfdInfo(status, info); diff --git a/src/3rdparty/forkfd/forkfd_linux.c b/src/3rdparty/forkfd/forkfd_linux.c index 27ab09f748..87acdc3341 100644 --- a/src/3rdparty/forkfd/forkfd_linux.c +++ b/src/3rdparty/forkfd/forkfd_linux.c @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 Intel Corporation. +** Copyright (C) 2020 Intel Corporation. ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal @@ -51,6 +51,10 @@ # define P_PIDFD 3 #endif +// in forkfd.c +static int convertForkfdWaitFlagsToWaitFlags(int ffdoptions); +static void convertStatusToForkfdInfo(int status, struct forkfd_info *info); + static ffd_atomic_int system_forkfd_state = FFD_ATOMIC_INIT(0); static int sys_waitid(int which, int pid_or_pidfd, siginfo_t *infop, int options, @@ -162,15 +166,20 @@ int system_forkfd(int flags, pid_t *ppid, int *system) return pidfd; } -int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage) +int system_forkfd_wait(int ffd, struct forkfd_info *info, int ffdoptions, struct rusage *rusage) { siginfo_t si; - int options = WEXITED | __WALL; - int ret = fcntl(ffd, F_GETFL); - if (ret == -1) - return ret; - if (ret & O_NONBLOCK) - options |= WNOHANG; + int ret; + int options = __WALL | convertForkfdWaitFlagsToWaitFlags(ffdoptions); + + if ((options & WNOHANG) == 0) { + /* check if the file descriptor is non-blocking */ + ret = fcntl(ffd, F_GETFL); + if (ret == -1) + return ret; + if (ret & O_NONBLOCK) + options |= WNOHANG; + } ret = sys_waitid(P_PIDFD, ffd, &si, options, rusage); if (ret == -1 && errno == ECHILD) { -- cgit v1.2.3 From ba5e2ce49a43c7d68e2ffa57b9e7f8d5d7fafe2f Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 21 Feb 2020 20:40:35 -0800 Subject: forkfd: fix forkfd_wait when FFD_USE_FORK was active If we detected that the OS supports a version of system forkfd (Linux pidfd, FreeBSD procdesc), the forkfd_wait() function was using only the system waiting implementation, which of course can't work for file descriptors created with FFD_USE_FORK. So just detect EBADF and attempt again. If the file descriptor is neither one of our pipes nor a system forkfd, bad things will happen... Fixes: QTBUG-82351 Change-Id: I4e559af2a9a1455ab770fffd15f59fb3160b22eb Reviewed-by: Edward Welbourne Reviewed-by: Oswald Buddenhagen --- src/3rdparty/forkfd/forkfd.c | 28 +++++++++++-- tests/auto/corelib/io/qprocess/tst_qprocess.cpp | 56 ++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c index 795aa9dd68..80f0e448e6 100644 --- a/src/3rdparty/forkfd/forkfd.c +++ b/src/3rdparty/forkfd/forkfd.c @@ -29,6 +29,11 @@ #include "forkfd.h" +/* Macros fine-tuning the build: */ +//#define FORKFD_NO_FORKFD 1 /* disable the forkfd() function */ +//#define FORKFD_NO_SPAWNFD 1 /* disable the spawnfd() function */ +//#define FORKFD_DISABLE_FORK_FALLBACK 1 /* disable falling back to fork() from system_forkfd() */ + #include #if defined(__OpenBSD__) || defined(__NetBSD__) # include @@ -96,6 +101,16 @@ static int system_has_forkfd(void); static int system_forkfd(int flags, pid_t *ppid, int *system); static int system_forkfd_wait(int ffd, struct forkfd_info *info, int ffdwoptions, struct rusage *rusage); +static int disable_fork_fallback(void) +{ +#ifdef FORKFD_DISABLE_FORK_FALLBACK + /* if there's no system forkfd, we have to use the fallback */ + return system_has_forkfd(); +#else + return false; +#endif +} + #define CHILDREN_IN_SMALL_ARRAY 16 #define CHILDREN_IN_BIG_ARRAY 256 #define sizeofarray(array) (sizeof(array)/sizeof(array[0])) @@ -629,9 +644,12 @@ int forkfd(int flags, pid_t *ppid) int efd; #endif + if (disable_fork_fallback()) + flags &= ~FFD_USE_FORK; + if ((flags & FFD_USE_FORK) == 0) { fd = system_forkfd(flags, ppid, &ret); - if (ret) + if (ret || disable_fork_fallback()) return fd; } @@ -815,8 +833,12 @@ int forkfd_wait4(int ffd, struct forkfd_info *info, int options, struct rusage * struct pipe_payload payload; int ret; - if (system_has_forkfd()) - return system_forkfd_wait(ffd, info, options, rusage); + if (system_has_forkfd()) { + /* if this is one of our pipes, not a procdesc/pidfd, we'll get an EBADF */ + ret = system_forkfd_wait(ffd, info, options, rusage); + if (disable_fork_fallback() || ret != -1 || errno != EBADF) + return ret; + } ret = read(ffd, &payload, sizeof(payload)); if (ret == -1) diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp index 3de1bef789..9495631c23 100644 --- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp +++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2020 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -40,6 +40,12 @@ #include #include #include + +#include +#ifdef Q_OS_UNIX +# include +#endif + #include typedef void (QProcess::*QProcessFinishedSignal1)(int); @@ -59,6 +65,7 @@ private slots: void getSetCheck(); void constructing(); void simpleStart(); + void setupChildProcess(); void startWithOpen(); void startWithOldOpen(); void execute(); @@ -277,6 +284,51 @@ void tst_QProcess::simpleStart() QCOMPARE(qvariant_cast(spy.at(2).at(0)), QProcess::NotRunning); } +void tst_QProcess::setupChildProcess() +{ + /* This test exists because in Qt 5.15, the Unix version of QProcess has + * some code that depends on whether it's an actual QProcess or a + * derived class */ + static const char setupChildMessage[] = "Called from setupChildProcess()"; + class DerivedProcessClass : public QProcess { + public: + int fd; + DerivedProcessClass(int fd) : fd(fd) + { + } + + protected: + void setupChildProcess() override + { + QT_WRITE(fd, setupChildMessage, sizeof(setupChildMessage) - 1); + QT_CLOSE(fd); + } + }; + + int pipes[2] = { -1 , -1 }; +#ifdef Q_OS_UNIX + QVERIFY(qt_safe_pipe(pipes) == 0); +#endif + + DerivedProcessClass process(pipes[1]); + process.start("testProcessNormal/testProcessNormal"); + if (process.state() != QProcess::Starting) + QCOMPARE(process.state(), QProcess::Running); + QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); + +#ifdef Q_OS_UNIX + char buf[sizeof setupChildMessage] = {}; + qt_safe_close(pipes[1]); + QCOMPARE(qt_safe_read(pipes[0], buf, sizeof(buf)), qint64(sizeof(setupChildMessage) - 1)); + QCOMPARE(buf, setupChildMessage); + qt_safe_close(pipes[0]); +#endif + + QVERIFY2(process.waitForFinished(5000), qPrintable(process.errorString())); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); + QCOMPARE(process.exitCode(), 0); +} + void tst_QProcess::startWithOpen() { QProcess p; -- cgit v1.2.3 From defd49f7bfa07395a2f2a69e2f9db94b8ac5d64c Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 17 Feb 2020 11:41:16 -0800 Subject: forkfd: add FFD_VFORK_SEMANTICS flag This flag asks forkfd() to use vfork semantics wherever available. That is, suspend the calling process execution until the child either does an execve(2) or _exit(2). The advantage of that is that it puts lower pressure on the OS VMM system, as the number of pages that need to be copy-on-write duplicated is much smaller (still not zero, as at least the stack in the child will be written to). However, the only implementation that supports using this flag for now is Linux's pidfd. It would be possible to add to FreeBSD, but pdfork(2) does not have a flag for this behavior -- if it gets one, we can add support for it later. Everywhere else, we need to force the child to not exit until we store the child process's PID in the ProcessInfo structure we allocated, which means the parent process must run before we even return from forkfd(). Change-Id: I1bee3bc466a04f19bd6efffd15f447f28c201aa9 Reviewed-by: Edward Welbourne --- src/3rdparty/forkfd/forkfd.c | 8 +++++++- src/3rdparty/forkfd/forkfd.h | 7 ++++--- src/3rdparty/forkfd/forkfd_linux.c | 5 ++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c index 80f0e448e6..50784deaa5 100644 --- a/src/3rdparty/forkfd/forkfd.c +++ b/src/3rdparty/forkfd/forkfd.c @@ -611,12 +611,18 @@ static int create_pipe(int filedes[], int flags) * descriptor. You probably want to set this flag, since forkfd() does not work * if the original parent process dies. * - * @li @C FFD_USE_FORK Tell forkfd() to actually call fork() instead of a + * @li @c FFD_USE_FORK Tell forkfd() to actually call fork() instead of a * different system implementation that may be available. On systems where a * different implementation is available, its behavior may differ from that of * fork(), such as not calling the functions registered with pthread_atfork(). * If that's necessary, pass this flag. * + * @li @c FFD_VFORK_SEMANTICS Tell forkfd() to use semantics similar to + * vfork(), if that's available. For example, on Linux with pidfd support + * available, this will add the CLONE_VFORK option. On most other systems, + * including Linux without pidfd support, this option does nothing, as using + * the actual vfork() system call would cause a race condition. + * * The file descriptor returned by forkfd() supports the following operations: * * @li read(2) When the child process exits, then the buffer supplied to diff --git a/src/3rdparty/forkfd/forkfd.h b/src/3rdparty/forkfd/forkfd.h index 205928cc2b..a864b59861 100644 --- a/src/3rdparty/forkfd/forkfd.h +++ b/src/3rdparty/forkfd/forkfd.h @@ -38,9 +38,10 @@ extern "C" { #endif -#define FFD_CLOEXEC 1 -#define FFD_NONBLOCK 2 -#define FFD_USE_FORK 4 +#define FFD_CLOEXEC 1 +#define FFD_NONBLOCK 2 +#define FFD_USE_FORK 4 +#define FFD_VFORK_SEMANTICS 8 #define FFD_CHILD_PROCESS (-2) diff --git a/src/3rdparty/forkfd/forkfd_linux.c b/src/3rdparty/forkfd/forkfd_linux.c index 87acdc3341..c4f723343f 100644 --- a/src/3rdparty/forkfd/forkfd_linux.c +++ b/src/3rdparty/forkfd/forkfd_linux.c @@ -147,7 +147,10 @@ int system_forkfd(int flags, pid_t *ppid, int *system) } *system = 1; - pid = sys_clone(CLONE_PIDFD, &pidfd); + unsigned long cloneflags = CLONE_PIDFD; + if (flags & FFD_VFORK_SEMANTICS) + cloneflags |= CLONE_VFORK; + pid = sys_clone(cloneflags, &pidfd); if (ppid) *ppid = pid; -- cgit v1.2.3 From 028ddf3633f394c930ddb82551ef2d1fa9b1a04a Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 17 Feb 2020 11:56:46 -0800 Subject: QProcess/Linux: use the FFD_VFORK_SEMANTICS flag ... when we are not using the FFD_USE_FORK flag. We use the FFD_USE_FORK flag when we have user code to run in setupChildProcess(). This code is enabled for all Unix, but forkfd() honors this flag only on Linux >= 5.4. See the commit adding the flag for more information on what the flag does and see the comment in this commit on why it's safe to use it. Fixes: QTBUG-17331 Change-Id: I1bee3bc466a04f19bd6efffd15f448cb23ce1e91 Reviewed-by: Oswald Buddenhagen --- src/corelib/io/qprocess_unix.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 2186f23ab6..930007ff04 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -450,12 +450,19 @@ void QProcessPrivate::startProcess() workingDirPtr = encodedWorkingDirectory.constData(); } - // Start the process manager, and fork off the child process. - // ### Qt6: revisit whether the change in behavior due to not using fork() - // is acceptable for derived classes. + // Select FFD_USE_FORK and FFD_VFORK_SEMANTICS based on whether there's + // user code running in the child process: if there is, we don't know what + // the user will want to do, so we err on the safe side and request an + // actual fork() (for example, the user could attempt to do some + // synchronization with the parent process). But if there isn't, then our + // code in execChild() is just a handful of dup2() and a chdir(), so it's + // safe with vfork semantics: suspend the parent execution until the child + // either execve()s or _exit()s. int ffdflags = FFD_CLOEXEC; if (typeid(*q) != typeid(QProcess)) ffdflags |= FFD_USE_FORK; + else + ffdflags |= FFD_VFORK_SEMANTICS; pid_t childPid; forkfd = ::forkfd(ffdflags , &childPid); int lastForkErrno = errno; -- cgit v1.2.3 From b7da66132bdd196c4f0b0e0fdf53f9e3b9a8bdaf Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 7 Mar 2020 07:31:42 -0800 Subject: QCborValue: create a wrapper to set the QCborStreamReader error state The next commit will need to do so from outside QCborContainerPrivate, where QCborStreamReader::d can't be accessed (private). Change-Id: Iaa63461109844e978376fffd15fa0f6f04081bf2 Reviewed-by: Lars Knoll --- src/corelib/serialization/qcborvalue.cpp | 17 +++++++++++------ src/corelib/serialization/qcborvalue_p.h | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index 4052bfa22e..5d4dc6ad5e 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -833,8 +833,6 @@ static QCborValue::Type convertToExtendedType(QCborContainerPrivate *d) return QCborValue::Tag; } -// in qcborstream.cpp -extern void qt_cbor_stream_set_error(QCborStreamReaderPrivate *d, QCborError error); static void writeDoubleToCbor(QCborStreamWriter &writer, double d, QCborValue::EncodingOptions opt) { @@ -1472,6 +1470,13 @@ static QCborValue taggedValueFromCbor(QCborStreamReader &reader) return QCborContainerPrivate::makeValue(type, -1, d); } +// in qcborstream.cpp +extern void qt_cbor_stream_set_error(QCborStreamReaderPrivate *d, QCborError error); +inline void QCborContainerPrivate::setErrorInReader(QCborStreamReader &reader, QCborError error) +{ + qt_cbor_stream_set_error(reader.d.data(), error); +} + void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) { auto addByteData_local = [this](QByteArray::size_type len) -> qint64 { @@ -1516,7 +1521,7 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) return; // error if (len != rawlen) { // truncation - qt_cbor_stream_set_error(reader.d.data(), { QCborError::DataTooLarge }); + setErrorInReader(reader, { QCborError::DataTooLarge }); return; } @@ -1526,7 +1531,7 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) e.value = addByteData_local(len); if (e.value < 0) { // overflow - qt_cbor_stream_set_error(reader.d.data(), { QCborError::DataTooLarge }); + setErrorInReader(reader, { QCborError::DataTooLarge }); return; } } @@ -1540,7 +1545,7 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) auto utf8result = QUtf8::isValidUtf8(dataPtr() + data.size() - len, len); if (!utf8result.isValidUtf8) { r.status = QCborStreamReader::Error; - qt_cbor_stream_set_error(reader.d.data(), { QCborError::InvalidUtf8String }); + setErrorInReader(reader, { QCborError::InvalidUtf8String }); break; } isAscii = isAscii && utf8result.isValidAscii; @@ -1564,7 +1569,7 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) // error r.status = QCborStreamReader::Error; - qt_cbor_stream_set_error(reader.d.data(), { QCborError::DataTooLarge }); + setErrorInReader(reader, { QCborError::DataTooLarge }); } if (r.status == QCborStreamReader::Error) { diff --git a/src/corelib/serialization/qcborvalue_p.h b/src/corelib/serialization/qcborvalue_p.h index a74ac2ba10..6d41586594 100644 --- a/src/corelib/serialization/qcborvalue_p.h +++ b/src/corelib/serialization/qcborvalue_p.h @@ -408,6 +408,7 @@ public: void decodeValueFromCbor(QCborStreamReader &reader); void decodeFromCbor(QCborStreamReader &reader); void decodeStringFromCbor(QCborStreamReader &reader); + static inline void setErrorInReader(QCborStreamReader &reader, QCborError error); }; QT_END_NAMESPACE -- cgit v1.2.3 From 02d595946faa7a21f6aa4109227f7e90db20ae7a Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 7 Mar 2020 07:38:51 -0800 Subject: QCborValue::fromCbor: Apply a recursion limit to decoding A simple 16k file can produce deep enough recursion in Qt to cause stack overflow. So prevent that. I tested 4096 recursions just fine on my Linux system (8 MB stack), but decided 1024 was sufficient, as this code will also be run on embedded systems that could have smaller stacks. [ChangeLog][QtCore][QCborValue] fromCbor() now limits decoding to at most 1024 nested maps, arrays, and tags to prevent stack overflows. This should be sufficient for most uses of CBOR. An API to limit further or to relax the limit will be provided in 5.15. Meanwhile, if decoding more is required, QCborStreamReader can be used (note that each level of map and array allocates memory). Change-Id: Iaa63461109844e978376fffd15fa0fbefbf607a2 Reviewed-by: Lars Knoll --- src/corelib/serialization/qcborvalue.cpp | 39 +++++++++++----- src/corelib/serialization/qcborvalue_p.h | 4 +- .../serialization/qcborvalue/tst_qcborvalue.cpp | 54 ++++++++++++++++++++++ 3 files changed, 84 insertions(+), 13 deletions(-) diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index 5d4dc6ad5e..23b4a15c0c 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -1438,23 +1438,33 @@ static Element decodeBasicValueFromCbor(QCborStreamReader &reader) return e; } -static inline QCborContainerPrivate *createContainerFromCbor(QCborStreamReader &reader) +static inline QCborContainerPrivate *createContainerFromCbor(QCborStreamReader &reader, int remainingRecursionDepth) { + if (Q_UNLIKELY(remainingRecursionDepth == 0)) { + QCborContainerPrivate::setErrorInReader(reader, { QCborError::NestingTooDeep }); + return nullptr; + } + auto d = new QCborContainerPrivate; d->ref.storeRelaxed(1); - d->decodeFromCbor(reader); + d->decodeContainerFromCbor(reader, remainingRecursionDepth - 1); return d; } -static QCborValue taggedValueFromCbor(QCborStreamReader &reader) +static QCborValue taggedValueFromCbor(QCborStreamReader &reader, int remainingRecursionDepth) { + if (Q_UNLIKELY(remainingRecursionDepth == 0)) { + QCborContainerPrivate::setErrorInReader(reader, { QCborError::NestingTooDeep }); + return QCborValue::Invalid; + } + auto d = new QCborContainerPrivate; d->append(reader.toTag()); reader.next(); if (reader.lastError() == QCborError::NoError) { // decode tagged value - d->decodeValueFromCbor(reader); + d->decodeValueFromCbor(reader, remainingRecursionDepth - 1); } QCborValue::Type type; @@ -1595,9 +1605,10 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) elements.append(e); } -void QCborContainerPrivate::decodeValueFromCbor(QCborStreamReader &reader) +void QCborContainerPrivate::decodeValueFromCbor(QCborStreamReader &reader, int remainingRecursionDepth) { - switch (reader.type()) { + QCborStreamReader::Type t = reader.type(); + switch (t) { case QCborStreamReader::UnsignedInteger: case QCborStreamReader::NegativeInteger: case QCborStreamReader::SimpleType: @@ -1614,15 +1625,19 @@ void QCborContainerPrivate::decodeValueFromCbor(QCborStreamReader &reader) case QCborStreamReader::Array: case QCborStreamReader::Map: + return append(makeValue(t == QCborStreamReader::Array ? QCborValue::Array : QCborValue::Map, -1, + createContainerFromCbor(reader, remainingRecursionDepth), + MoveContainer)); + case QCborStreamReader::Tag: - return append(QCborValue::fromCbor(reader)); + return append(taggedValueFromCbor(reader, remainingRecursionDepth)); case QCborStreamReader::Invalid: return; // probably a decode error } } -void QCborContainerPrivate::decodeFromCbor(QCborStreamReader &reader) +void QCborContainerPrivate::decodeContainerFromCbor(QCborStreamReader &reader, int remainingRecursionDepth) { int mapShift = reader.isMap() ? 1 : 0; if (reader.isLengthKnown()) { @@ -1640,7 +1655,7 @@ void QCborContainerPrivate::decodeFromCbor(QCborStreamReader &reader) return; while (reader.hasNext() && reader.lastError() == QCborError::NoError) - decodeValueFromCbor(reader); + decodeValueFromCbor(reader, remainingRecursionDepth); if (reader.lastError() == QCborError::NoError) reader.leaveContainer(); @@ -2340,6 +2355,8 @@ QCborValueRef QCborValue::operator[](qint64 key) return { container, index }; } +enum { MaximumRecursionDepth = 1024 }; + /*! Decodes one item from the CBOR stream found in \a reader and returns the equivalent representation. This function is recursive: if the item is a map @@ -2400,12 +2417,12 @@ QCborValue QCborValue::fromCbor(QCborStreamReader &reader) case QCborStreamReader::Map: result.n = -1; result.t = reader.isArray() ? Array : Map; - result.container = createContainerFromCbor(reader); + result.container = createContainerFromCbor(reader, MaximumRecursionDepth); break; // tag case QCborStreamReader::Tag: - result = taggedValueFromCbor(reader); + result = taggedValueFromCbor(reader, MaximumRecursionDepth); break; } diff --git a/src/corelib/serialization/qcborvalue_p.h b/src/corelib/serialization/qcborvalue_p.h index 6d41586594..2358626541 100644 --- a/src/corelib/serialization/qcborvalue_p.h +++ b/src/corelib/serialization/qcborvalue_p.h @@ -405,8 +405,8 @@ public: elements.remove(idx); } - void decodeValueFromCbor(QCborStreamReader &reader); - void decodeFromCbor(QCborStreamReader &reader); + void decodeValueFromCbor(QCborStreamReader &reader, int remainiingStackDepth); + void decodeContainerFromCbor(QCborStreamReader &reader, int remainingStackDepth); void decodeStringFromCbor(QCborStreamReader &reader); static inline void setErrorInReader(QCborStreamReader &reader, QCborError error); }; diff --git a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp index d5a9012f9f..49bb9cc144 100644 --- a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp +++ b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp @@ -102,6 +102,8 @@ private slots: void fromCborStreamReaderIODevice(); void validation_data(); void validation(); + void recursionLimit_data(); + void recursionLimit(); void toDiagnosticNotation_data(); void toDiagnosticNotation(); @@ -1720,6 +1722,58 @@ void tst_QCborValue::validation() } } +void tst_QCborValue::recursionLimit_data() +{ + constexpr int RecursionAttempts = 4096; + QTest::addColumn("data"); + QByteArray arrays(RecursionAttempts, char(0x81)); + QByteArray _arrays(RecursionAttempts, char(0x9f)); + QByteArray maps(RecursionAttempts, char(0xa1)); + QByteArray _maps(RecursionAttempts, char(0xbf)); + QByteArray tags(RecursionAttempts, char(0xc0)); + + QTest::newRow("array-nesting-too-deep") << arrays; + QTest::newRow("_array-nesting-too-deep") << _arrays; + QTest::newRow("map-nesting-too-deep") << maps; + QTest::newRow("_map-nesting-too-deep") << _maps; + QTest::newRow("tag-nesting-too-deep") << tags; + + QByteArray mixed(5 * RecursionAttempts, Qt::Uninitialized); + char *ptr = mixed.data(); + for (int i = 0; i < RecursionAttempts; ++i) { + quint8 type = qBound(quint8(QCborStreamReader::Array), quint8(i & 0x80), quint8(QCborStreamReader::Tag)); + quint8 additional_info = i & 0x1f; + if (additional_info == 0x1f) + (void)additional_info; // leave it + else if (additional_info > 0x1a) + additional_info = 0x1a; + else if (additional_info < 1) + additional_info = 1; + + *ptr++ = type | additional_info; + if (additional_info == 0x18) { + *ptr++ = uchar(i); + } else if (additional_info == 0x19) { + qToBigEndian(ushort(i), ptr); + ptr += 2; + } else if (additional_info == 0x1a) { + qToBigEndian(uint(i), ptr); + ptr += 4; + } + } + + QTest::newRow("mixed-nesting-too-deep") << mixed; +} + +void tst_QCborValue::recursionLimit() +{ + QFETCH(QByteArray, data); + + QCborParserError error; + QCborValue decoded = QCborValue::fromCbor(data, &error); + QCOMPARE(error.error, QCborError::NestingTooDeep); +} + void tst_QCborValue::toDiagnosticNotation_data() { QTest::addColumn("v"); -- cgit v1.2.3 From f581b041199ba7c825fbffd9110727360d1f2668 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 20 Mar 2020 18:43:09 -0300 Subject: QCborValue: apply a simple optimization to avoid unnecessary allocations If the map or array is known to be empty, we don't need to allocate a QCborContainerPrivate. Change-Id: Ief61acdfbe4d4b5ba1f0fffd15fe212b6a6e77c3 Reviewed-by: Edward Welbourne Reviewed-by: Lars Knoll --- src/corelib/serialization/qcborvalue.cpp | 56 +++++++++++++++++--------------- src/corelib/serialization/qcborvalue_p.h | 1 - 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index 23b4a15c0c..f4ceba3836 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -1445,9 +1445,35 @@ static inline QCborContainerPrivate *createContainerFromCbor(QCborStreamReader & return nullptr; } - auto d = new QCborContainerPrivate; - d->ref.storeRelaxed(1); - d->decodeContainerFromCbor(reader, remainingRecursionDepth - 1); + QCborContainerPrivate *d = nullptr; + int mapShift = reader.isMap() ? 1 : 0; + if (reader.isLengthKnown()) { + quint64 len = reader.length(); + + // Clamp allocation to 1M elements (avoids crashing due to corrupt + // stream or loss of precision when converting from quint64 to + // QVector::size_type). + len = qMin(len, quint64(1024 * 1024 - 1)); + if (len) { + d = new QCborContainerPrivate; + d->ref.storeRelaxed(1); + d->elements.reserve(qsizetype(len) << mapShift); + } + } else { + d = new QCborContainerPrivate; + d->ref.storeRelaxed(1); + } + + reader.enterContainer(); + if (reader.lastError() != QCborError::NoError) + return d; + + while (reader.hasNext() && reader.lastError() == QCborError::NoError) + d->decodeValueFromCbor(reader, remainingRecursionDepth - 1); + + if (reader.lastError() == QCborError::NoError) + reader.leaveContainer(); + return d; } @@ -1637,30 +1663,6 @@ void QCborContainerPrivate::decodeValueFromCbor(QCborStreamReader &reader, int r } } -void QCborContainerPrivate::decodeContainerFromCbor(QCborStreamReader &reader, int remainingRecursionDepth) -{ - int mapShift = reader.isMap() ? 1 : 0; - if (reader.isLengthKnown()) { - quint64 len = reader.length(); - - // Clamp allocation to 1M elements (avoids crashing due to corrupt - // stream or loss of precision when converting from quint64 to - // QVector::size_type). - len = qMin(len, quint64(1024 * 1024 - 1)); - elements.reserve(qsizetype(len) << mapShift); - } - - reader.enterContainer(); - if (reader.lastError() != QCborError::NoError) - return; - - while (reader.hasNext() && reader.lastError() == QCborError::NoError) - decodeValueFromCbor(reader, remainingRecursionDepth); - - if (reader.lastError() == QCborError::NoError) - reader.leaveContainer(); -} - /*! Creates a QCborValue with byte array value \a ba. The value can later be retrieved using toByteArray(). diff --git a/src/corelib/serialization/qcborvalue_p.h b/src/corelib/serialization/qcborvalue_p.h index 2358626541..041a20e746 100644 --- a/src/corelib/serialization/qcborvalue_p.h +++ b/src/corelib/serialization/qcborvalue_p.h @@ -406,7 +406,6 @@ public: } void decodeValueFromCbor(QCborStreamReader &reader, int remainiingStackDepth); - void decodeContainerFromCbor(QCborStreamReader &reader, int remainingStackDepth); void decodeStringFromCbor(QCborStreamReader &reader); static inline void setErrorInReader(QCborStreamReader &reader, QCborError error); }; -- cgit v1.2.3 From f22c929c8a1e9575ba9f99acdbb061e12d4da4a2 Mon Sep 17 00:00:00 2001 From: Sona Kurazyan Date: Tue, 17 Mar 2020 16:40:34 +0100 Subject: Make tst_QRandomGenerator::qualityReal() test more stable Increasing the sample size of randomly generated test samples reduces the probability of small deviations from the expected uniform distribution. On my machine with the new values the test fails approximately once per 3000 consecutive runs, instead of failing once per 300. Change-Id: I4d1815504c353290a2fb350b3fd1cbb802f8d559 Reviewed-by: Lars Knoll Reviewed-by: Thiago Macieira --- tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp index 6f9dcc08f9..5bbde1ef09 100644 --- a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp +++ b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp @@ -671,7 +671,7 @@ void tst_QRandomGenerator::qualityReal() RandomGenerator rng(control); enum { - SampleSize = 160, + SampleSize = 16000, // Expected value: sample size times proportion of the range: PerfectOctile = SampleSize / 8, @@ -679,8 +679,8 @@ void tst_QRandomGenerator::qualityReal() // Variance is (1 - proportion of range) * expected; sqrt() for standard deviations. // Should usually be within twice that and almost never outside four times: - RangeHalf = 25, // floor(4 * sqrt((1 - 0.5) * PerfectHalf)) - RangeOctile = 16 // floor(4 * sqrt((1 - 0.125) * PerfectOctile)) + RangeHalf = 252, // floor(4 * sqrt((1 - 0.5) * PerfectHalf)) + RangeOctile = 167 // floor(4 * sqrt((1 - 0.125) * PerfectOctile)) }; double data[SampleSize]; -- cgit v1.2.3 From 3f3e200aef80ac33e6bf116c656ebc28d5918836 Mon Sep 17 00:00:00 2001 From: Paul Wicking Date: Mon, 23 Mar 2020 12:56:02 +0100 Subject: Doc: Fix coverity warnings in SQL snippets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: QTBUG-83008 Change-Id: I126bc04719cd221a3d80ae825fca44e63aeec934 Reviewed-by: Topi Reiniö --- src/sql/doc/snippets/code/doc_src_sql-driver.cpp | 11 ++++++----- src/sql/doc/snippets/sqldatabase/sqldatabase.cpp | 7 ++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/sql/doc/snippets/code/doc_src_sql-driver.cpp b/src/sql/doc/snippets/code/doc_src_sql-driver.cpp index 076a3367dc..6b5a5e0335 100644 --- a/src/sql/doc/snippets/code/doc_src_sql-driver.cpp +++ b/src/sql/doc/snippets/code/doc_src_sql-driver.cpp @@ -60,8 +60,8 @@ void testProc() QSqlQuery q; q.exec("call qtestproc (@outval1, @outval2)"); q.exec("select @outval1, @outval2"); -q.next(); -qDebug() << q.value(0) << q.value(1); // outputs "42" and "43" +if (q.next()) + qDebug() << q.value(0) << q.value(1); // outputs "42" and "43" //! [2] } @@ -87,7 +87,8 @@ db.setDatabaseName("C:\\test.gdb"); //! [25] // connect to database using the Latin-1 character set db.setConnectOptions("ISC_DPB_LC_CTYPE=Latin1"); -db.open(); +if (db.open()) + qDebug("The database connection is open."); //! [25] } @@ -96,8 +97,8 @@ void exProc() //! [26] QSqlQuery q; q.exec("execute procedure my_procedure"); -q.next(); -qDebug() << q.value(0); // outputs the first RETURN/OUT value +if (q.next()) + qDebug() << q.value(0); // outputs the first RETURN/OUT value //! [26] qDebug( \ diff --git a/src/sql/doc/snippets/sqldatabase/sqldatabase.cpp b/src/sql/doc/snippets/sqldatabase/sqldatabase.cpp index 2039007c4a..0c09d76145 100644 --- a/src/sql/doc/snippets/sqldatabase/sqldatabase.cpp +++ b/src/sql/doc/snippets/sqldatabase/sqldatabase.cpp @@ -207,7 +207,7 @@ void QSqlQuery_snippets() QMap sqlIterator(query.boundValues()); for (auto i = sqlIterator.begin(); i != sqlIterator.end(); ++i) { cout << i.key().toUtf8().data() << ": " - << i.value().toString().toUtf8().data() << Qt::endl; + << i.value().toString().toUtf8().data() << "\n"; } //! [14] } @@ -217,7 +217,7 @@ void QSqlQuery_snippets() //! [15] QList list = query.boundValues().values(); for (int i = 0; i < list.size(); ++i) - cout << i << ": " << list.at(i).toString().toUtf8().data() << Qt::endl; + cout << i << ": " << list.at(i).toString().toUtf8().data() << "\n"; //! [15] } } @@ -274,6 +274,7 @@ void QSqlTableModel_snippets() model.select(); int salary = model.record(4).value("salary").toInt(); //! [25] + Q_UNUSED(salary); } } @@ -514,7 +515,7 @@ public: const QString & /* password */, const QString & /* host */, int /* port */, const QString & /* options */) override { return false; } - void close() {} + void close() override {} QSqlResult *createResult() const override { return new XyzResult(this); } }; //! [48] -- cgit v1.2.3 From 85a1582412ada0a8837fef76a23d569e95fa8d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Fri, 13 Mar 2020 11:36:29 +0100 Subject: wasm: add braces around multiline else case From clang warning: qwasmcompositor.cpp:335:13: warning: misleading indentation; statement is not part of the previous 'if' [-Wmisleading-indentation] offset += (delta +delta); Change-Id: Id372dfdd8fda1c705c61a24c26843dfb33fba40c Reviewed-by: Lorn Potter --- src/plugins/platforms/wasm/qwasmcompositor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index 2f0b0414d9..0ece812972 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -328,11 +328,12 @@ QRect QWasmCompositor::titlebarRect(QWasmTitleBarOptions tb, QWasmCompositor::Su } break; case SC_TitleBarNormalButton: - if (isMinimized && (tb.flags & Qt::WindowMinimizeButtonHint)) + if (isMinimized && (tb.flags & Qt::WindowMinimizeButtonHint)) { offset += delta; - else if (isMaximized && (tb.flags & Qt::WindowMaximizeButtonHint)) + } else if (isMaximized && (tb.flags & Qt::WindowMaximizeButtonHint)) { ret.adjust(0, 0, -delta*2, 0); offset += (delta +delta); + } break; case SC_TitleBarSysMenu: if (tb.flags & Qt::WindowSystemMenuHint) { -- cgit v1.2.3 From fc49325c806d03b609e0e0206086edadc7f2782e Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 25 Mar 2020 14:59:51 +0100 Subject: qmake: Document QML_FOREIGN_METATYPES Task-number: QTBUG-82709 Change-Id: I0ca648114adbbed1ab3c6406e9f14f60f7924e96 Reviewed-by: Fabian Kosmale --- qmake/doc/src/qmake-manual.qdoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qmake/doc/src/qmake-manual.qdoc b/qmake/doc/src/qmake-manual.qdoc index f1e03eb407..c90092059f 100644 --- a/qmake/doc/src/qmake-manual.qdoc +++ b/qmake/doc/src/qmake-manual.qdoc @@ -2608,6 +2608,14 @@ Specifies the module name to be used for automatically generated QML type registrations. For more information, see \l {Defining QML Types from C++}. + \section1 QML_FOREIGN_METATYPES + + Specifies further JSON files with metatypes to be considered when generating + qmltypes files. Use this when external libraries provide types that are + exposed to QML, either directly or as base types or properties of other + types. Qt types will automatically be considered and don't have to be added + here. + \section1 QT Specifies the \l{All Modules}{Qt modules} that are used by your project. For -- cgit v1.2.3 From c91b7f15ce3e38f013c91cdd7cf66145ee00a521 Mon Sep 17 00:00:00 2001 From: Andre de la Rocha Date: Wed, 25 Mar 2020 02:24:32 +0100 Subject: Add "checkable" state to QAccessibleTableCell The information about whether a table/tree item may be checked is necessary to allow the platform code (in particular, Windows UI Automation layer) to make this information available to screen readers. Task-number: QTBUG-81919 Change-Id: Id68eea4a004788751404d70567222a2c531578aa Reviewed-by: Friedemann Kleint Reviewed-by: Oliver Wolff --- src/widgets/accessible/itemviews.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/widgets/accessible/itemviews.cpp b/src/widgets/accessible/itemviews.cpp index 5a7fdf9a03..677e56806a 100644 --- a/src/widgets/accessible/itemviews.cpp +++ b/src/widgets/accessible/itemviews.cpp @@ -1035,10 +1035,14 @@ QAccessible::State QAccessibleTableCell::state() const st.selected = true; if (view->selectionModel()->currentIndex() == m_index) st.focused = true; - if (m_index.model()->data(m_index, Qt::CheckStateRole).toInt() == Qt::Checked) + + QVariant checkState = m_index.model()->data(m_index, Qt::CheckStateRole); + if (checkState.toInt() == Qt::Checked) st.checked = true; Qt::ItemFlags flags = m_index.flags(); + if ((flags & Qt::ItemIsUserCheckable) && checkState.isValid()) + st.checkable = true; if (flags & Qt::ItemIsSelectable) { st.selectable = true; st.focusable = true; -- cgit v1.2.3 From 319ac188eb38555b90319b4bb282abad2f653b0b Mon Sep 17 00:00:00 2001 From: Andre de la Rocha Date: Wed, 25 Mar 2020 06:01:34 +0100 Subject: Windows QPA: Enable Toggle UIA pattern for all checkable controls This change allows the checked/unchecked state to be detected by screen readers for all controls with a checkable state. Task-number: QTBUG-81919 Change-Id: I604151397b4ae21297009c274ffe634723ebe783 Reviewed-by: Friedemann Kleint --- .../platforms/windows/uiautomation/qwindowsuiamainprovider.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp index 59360616a1..3962d2d27f 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp @@ -305,11 +305,9 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow } break; case UIA_TogglePatternId: - // Checkbox controls. - if (accessible->role() == QAccessible::CheckBox - || (accessible->role() == QAccessible::MenuItem && accessible->state().checkable)) { + // Checkboxes and other checkable controls. + if (accessible->state().checkable) *pRetVal = new QWindowsUiaToggleProvider(id()); - } break; case UIA_SelectionPatternId: // Lists of items. -- cgit v1.2.3 From b3b51a7665dae78db352ccdeb5f25f0e5df43c09 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 25 Mar 2020 10:57:14 +0100 Subject: Fix deprecation warnings for QInputDialog::getDouble() The way this was done didn't really make sense. The change added an overload with one additional argument. The deprecation warning would have now forced everybody to explicitly specify all arguments to avoid the warning. Instead, keep both overloads in 5.15, but document them as one method. Remove the old version in Qt6 and move the default arguments to the new version. Change-Id: I738d4d1b99cdf30db53acf14382a00cac74aa10a Reviewed-by: Kai Koehne --- src/widgets/dialogs/qinputdialog.cpp | 28 +--------------------------- src/widgets/dialogs/qinputdialog.h | 18 ++++++++---------- 2 files changed, 9 insertions(+), 37 deletions(-) diff --git a/src/widgets/dialogs/qinputdialog.cpp b/src/widgets/dialogs/qinputdialog.cpp index eeb0613d79..415214d6ea 100644 --- a/src/widgets/dialogs/qinputdialog.cpp +++ b/src/widgets/dialogs/qinputdialog.cpp @@ -1349,32 +1349,7 @@ int QInputDialog::getInt(QWidget *parent, const QString &title, const QString &l \sa getText(), getDouble(), getItem(), getMultiLineText() */ -#if QT_DEPRECATED_SINCE(5, 15) -/*! - Static convenience function to get a floating point number from the user. - - \a title is the text which is displayed in the title bar of the dialog. - \a label is the text which is shown to the user (it should say what should - be entered). - \a value is the default floating point number that the line edit will be - set to. - \a min and \a max are the minimum and maximum values the user may choose. - \a decimals is the maximum number of decimal places the number may have. - - If \a ok is nonnull, *\a ok will be set to true if the user pressed \uicontrol OK - and to false if the user pressed \uicontrol Cancel. The dialog's parent is - \a parent. The dialog will be modal and uses the widget \a flags. - - This function returns the floating point number which has been entered by - the user. - - Use this static function like this: - - \snippet dialogs/standarddialogs/dialog.cpp 1 - - \sa getText(), getInt(), getItem(), getMultiLineText() -*/ - +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && !defined(Q_QDOC) double QInputDialog::getDouble(QWidget *parent, const QString &title, const QString &label, double value, double min, double max, int decimals, bool *ok, Qt::WindowFlags flags) @@ -1383,7 +1358,6 @@ double QInputDialog::getDouble(QWidget *parent, const QString &title, const QStr } #endif /*! - \overload Static convenience function to get a floating point number from the user. \a title is the text which is displayed in the title bar of the dialog. diff --git a/src/widgets/dialogs/qinputdialog.h b/src/widgets/dialogs/qinputdialog.h index 6e2d6eebf3..a8696cb562 100644 --- a/src/widgets/dialogs/qinputdialog.h +++ b/src/widgets/dialogs/qinputdialog.h @@ -177,21 +177,19 @@ public: int minValue = -2147483647, int maxValue = 2147483647, int step = 1, bool *ok = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); -#if QT_DEPRECATED_SINCE(5, 15) - QT_DEPRECATED_X("This overload is deprecated. Use the overload that takes step as a final argument") - static double getDouble(QWidget *parent, const QString &title, const QString &label, double value = 0, - double minValue = -2147483647, double maxValue = 2147483647, - int decimals = 1, bool *ok = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); -#endif -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) || defined(Q_QDOC) static double getDouble(QWidget *parent, const QString &title, const QString &label, double value = 0, double minValue = -2147483647, double maxValue = 2147483647, int decimals = 1, bool *ok = nullptr, Qt::WindowFlags flags = Qt::WindowFlags(), double step = 1); #else - static double getDouble(QWidget *parent, const QString &title, const QString &label, double value, - double minValue, double maxValue, int decimals, bool *ok, Qt::WindowFlags flags, - double step); + static double getDouble(QWidget *parent, const QString &title, const QString &label, + double value = 0, double minValue = -2147483647, + double maxValue = 2147483647, int decimals = 1, bool *ok = nullptr, + Qt::WindowFlags flags = Qt::WindowFlags()); + static double getDouble(QWidget *parent, const QString &title, const QString &label, + double value, double minValue, double maxValue, int decimals, bool *ok, + Qt::WindowFlags flags, double step); #endif #if QT_DEPRECATED_SINCE(5, 0) -- cgit v1.2.3 From 5f87fb881383f6170ca2cf82958709b04372aae6 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Wed, 25 Mar 2020 18:06:31 +0100 Subject: QLineEdit: Include the horizontal margin in minimumSizeHint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The margin is respected in the sizeHint, but not in the minimumSizeHint. Since the latter should be the former for a single character (as per the documentation promising enough space for one character), the margin calculation needs to be identical. Adjusting the documentation nevertheless, as there are characters that won't fit either way. As reported, the permyriad character doesn't fit even with this fix on macOS, as the core graphics API we are using doesn't report a glyph-index for U+2031 (so our existing assumption that 'W' is the widest character is still the best we can do). Change-Id: I30573960c316cc7b8c9bbe3c3f4c6351792bed36 Fixes: QTBUG-82970 Reviewed-by: Tor Arne Vestbø --- src/widgets/widgets/qlineedit.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp index 06b40f9644..c578710f99 100644 --- a/src/widgets/widgets/qlineedit.cpp +++ b/src/widgets/widgets/qlineedit.cpp @@ -701,7 +701,7 @@ QSize QLineEdit::sizeHint() const /*! Returns a minimum size for the line edit. - The width returned is enough for at least one character. + The width returned is usually enough for at least one character. */ QSize QLineEdit::minimumSizeHint() const @@ -713,7 +713,7 @@ QSize QLineEdit::minimumSizeHint() const int h = fm.height() + qMax(2 * QLineEditPrivate::verticalMargin, fm.leading()) + tm.top() + tm.bottom() + d->topmargin + d->bottommargin; - int w = fm.maxWidth() + int w = fm.maxWidth() + 2 * QLineEditPrivate::horizontalMargin + tm.left() + tm.right() + d->leftmargin + d->rightmargin; QStyleOptionFrame opt; -- cgit v1.2.3 From a320b57f1d8532fc5ab00aed7a78c7f2f828da53 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Tue, 24 Mar 2020 14:23:02 +0100 Subject: Doc: Don't mention deprecated functions in QTabletEvent overview Change-Id: I5d41d6061403f2923d673376be7cf1250d0f0e82 Reviewed-by: Paul Wicking Reviewed-by: Shawn Rutledge --- src/gui/kernel/qevent.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 17aa550606..bda0949764 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -2225,14 +2225,14 @@ QVariant QInputMethodQueryEvent::value(Qt::InputMethodQuery query) const (pressing the stylus tip against the tablet surface is equivalent to a left mouse button). But tablet events also pass through some extra information that the tablet device driver provides; for example, you might want to do - subpixel rendering with higher resolution coordinates (\l hiResGlobalX() - and \l hiResGlobalY()), adjust color brightness based on the \l pressure() - of the tool against the tablet surface, use different brushes depending on - the type of tool in use (\l device()), modulate the brush shape in some way - according to the X-axis and Y-axis tilt of the tool with respect to the - tablet surface (\l xTilt() and \l yTilt()), and use a virtual eraser - instead of a brush if the user switches to the other end of a double-ended - stylus (\l pointerType()). + subpixel rendering with higher resolution coordinates (\l globalPosF()), + adjust color brightness based on the \l pressure() of the tool against the + tablet surface, use different brushes depending on the type of tool in use + (\l deviceType()), modulate the brush shape in some way according to the + X-axis and Y-axis tilt of the tool with respect to the tablet surface + (\l xTilt() and \l yTilt()), and use a virtual eraser instead of a brush if + the user switches to the other end of a double-ended stylus + (\l pointerType()). Every event contains an accept flag that indicates whether the receiver wants the event. You should call QTabletEvent::accept() if you handle the -- cgit v1.2.3 From c81b172781a35590a4f0345aa35e947d8e780694 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Tue, 17 Mar 2020 23:51:45 +0100 Subject: Doc: Mark gamma-related functions deprecated in QImageReader/Writer Change-Id: I0a9fbcca7a10a6555f5879cc4955f046eaa56602 Reviewed-by: Lars Knoll Reviewed-by: Paul Wicking --- src/gui/image/qimagereader.cpp | 4 +++- src/gui/image/qimagewriter.cpp | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index 3eb1e01863..5cb7e1328e 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -101,7 +101,7 @@ This can be disabled by setting the environment variable \c QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING. - \sa QImageWriter, QImageIOHandler, QImageIOPlugin, QMimeDatabase + \sa QImageWriter, QImageIOHandler, QImageIOPlugin, QMimeDatabase, QColorSpace \sa QImage::devicePixelRatio(), QPixmap::devicePixelRatio(), QIcon, QPainter::drawPixmap(), QPainter::drawImage(), Qt::AA_UseHighDpiPixmaps */ @@ -1152,6 +1152,7 @@ bool QImageReader::autoTransform() const #if QT_DEPRECATED_SINCE(5, 15) /*! \since 5.6 + \obsolete Use QColorSpace conversion on the QImage instead. This is an image format specific function that forces images with gamma information to be gamma corrected to \a gamma. For image formats @@ -1169,6 +1170,7 @@ void QImageReader::setGamma(float gamma) /*! \since 5.6 + \obsolete Use QImage::colorSpace() and QColorSpace::gamma() instead. Returns the gamma level of the decoded image. If setGamma() has been called and gamma correction is supported it will return the gamma set. diff --git a/src/gui/image/qimagewriter.cpp b/src/gui/image/qimagewriter.cpp index 6e74b23f76..33f5e491c7 100644 --- a/src/gui/image/qimagewriter.cpp +++ b/src/gui/image/qimagewriter.cpp @@ -47,19 +47,18 @@ \ingroup painting \ingroup io - QImageWriter supports setting format specific options, such as the - gamma level, compression level and quality, prior to storing the + QImageWriter supports setting format specific options, such as + compression level and quality, prior to storing the image. If you do not need such options, you can use QImage::save() or QPixmap::save() instead. To store an image, you start by constructing a QImageWriter object. Pass either a file name or a device pointer, and the image format to QImageWriter's constructor. You can then set - several options, such as the gamma level (by calling setGamma()) - and quality (by calling setQuality()). canWrite() returns \c true if - QImageWriter can write the image (i.e., the image format is - supported and the device is open for writing). Call write() to - write the image to the device. + several options, such as quality (by calling setQuality()). + canWrite() returns \c true if QImageWriter can write the image + (i.e., the image format is supported and the device is open for + writing). Call write() to write the image to the device. If any error occurs when writing the image, write() will return false. You can then call error() to find the type of error that @@ -81,7 +80,7 @@ \snippet qimagewriter/main.cpp 0 - \sa QImageReader, QImageIOHandler, QImageIOPlugin + \sa QImageReader, QImageIOHandler, QImageIOPlugin, QColorSpace */ /*! @@ -500,6 +499,8 @@ int QImageWriter::compression() const #if QT_DEPRECATED_SINCE(5, 15) /*! + \obsolete Use QColorSpace conversion on the QImage instead. + This is an image format specific function that sets the gamma level of the image to \a gamma. For image formats that do not support setting the gamma level, this value is ignored. @@ -515,6 +516,8 @@ void QImageWriter::setGamma(float gamma) } /*! + \obsolete Use QImage::colorSpace() and QColorSpace::gamma() instead. + Returns the gamma level of the image. \sa setGamma() -- cgit v1.2.3 From e16137d2db2621372ce1e2ec4e88eb41fdc8fd0d Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Tue, 17 Mar 2020 23:23:29 +0100 Subject: Doc: Document QDBusReply copy constructor Change-Id: Ice71b8d48f92b9ecd3075fba1927d3657934b018 Reviewed-by: Lars Knoll Reviewed-by: Paul Wicking --- src/dbus/qdbusreply.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/dbus/qdbusreply.cpp b/src/dbus/qdbusreply.cpp index cd7193e02f..33bfadd701 100644 --- a/src/dbus/qdbusreply.cpp +++ b/src/dbus/qdbusreply.cpp @@ -74,6 +74,13 @@ QT_BEGIN_NAMESPACE \sa QDBusMessage, QDBusInterface */ +/*! + \fn template QDBusReply::QDBusReply(const QDBusReply &other) + \since 5.15 + + Constructs a copy of \a other. +*/ + /*! \fn template QDBusReply::QDBusReply(const QDBusMessage &reply) Automatically construct a QDBusReply object from the reply message \a reply, extracting the -- cgit v1.2.3 From 5a7a590e8a1b688e62101d21209a0eb7203b6d78 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Tue, 17 Mar 2020 23:11:17 +0100 Subject: Doc: Mark deprecated QMutableSetIterator functions as \obsolete Change-Id: I07a4ade20242fbe4139c0b88cf8728dd74628511 Reviewed-by: Lars Knoll Reviewed-by: Paul Wicking --- src/corelib/tools/qiterator.qdoc | 54 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/src/corelib/tools/qiterator.qdoc b/src/corelib/tools/qiterator.qdoc index ed39fe28c3..273370e797 100644 --- a/src/corelib/tools/qiterator.qdoc +++ b/src/corelib/tools/qiterator.qdoc @@ -762,6 +762,17 @@ \sa hasNext(), next(), peekPrevious() */ +/*! + \fn template bool QMutableSetIterator::hasPrevious() const + \obsolete Deprecated in order to align with std::unordered_set functionality. + + Returns \c true if there is at least one item behind the iterator, + i.e. the iterator is \e not at the front of the container; + otherwise returns \c false. + + \sa hasNext(), previous() +*/ + /*! \fn template bool QListIterator::hasPrevious() const \fn template bool QLinkedListIterator::hasPrevious() const \fn template bool QVectorIterator::hasPrevious() const @@ -769,7 +780,6 @@ \fn template bool QMutableListIterator::hasPrevious() const \fn template bool QMutableLinkedListIterator::hasPrevious() const \fn template bool QMutableVectorIterator::hasPrevious() const - \fn template bool QMutableSetIterator::hasPrevious() const Returns \c true if there is at least one item behind the iterator, i.e. the iterator is \e not at the front of the container; @@ -778,11 +788,23 @@ \sa hasNext(), previous() */ +/*! + \fn template const T &QMutableSetIterator::previous() + \obsolete Deprecated in order to align with std::unordered_set functionality. + + Returns the previous item and moves the iterator back by one + position. + + Calling this function on an iterator located at the front of the + container leads to undefined results. + + \sa hasPrevious(), peekPrevious(), next() +*/ + /*! \fn template const T &QListIterator::previous() \fn template const T &QLinkedListIterator::previous() \fn template const T &QVectorIterator::previous() \fn template const T &QSetIterator::previous() - \fn template const T &QMutableSetIterator::previous() Returns the previous item and moves the iterator back by one position. @@ -806,11 +828,22 @@ \sa hasPrevious(), peekPrevious(), next() */ +/*! + \fn template const T &QMutableSetIterator::peekPrevious() const + \obsolete Deprecated in order to align with std::unordered_set functionality. + + Returns the previous item without moving the iterator. + + Calling this function on an iterator located at the front of the + container leads to undefined results. + + \sa hasPrevious(), previous(), peekNext() +*/ + /*! \fn template const T &QListIterator::peekPrevious() const \fn template const T &QLinkedListIterator::peekPrevious() const \fn template const T &QVectorIterator::peekPrevious() const \fn template const T &QSetIterator::peekPrevious() const - \fn template const T &QMutableSetIterator::peekPrevious() const Returns the previous item without moving the iterator. @@ -832,6 +865,20 @@ \sa hasPrevious(), previous(), peekNext() */ +/*! + \fn template bool QMutableSetIterator::findNext(const T &value) + \obsolete Deprecated in order to align with std::unordered_set functionality. + + Searches for \a value starting from the current iterator position + forward. Returns \c true if \a value is found; otherwise returns \c false. + + After the call, if \a value was found, the iterator is positioned + just after the matching item; otherwise, the iterator is + positioned at the back of the container. + + \sa findPrevious() +*/ + /*! \fn template bool QListIterator::findNext(const T &value) \fn template bool QLinkedListIterator::findNext(const T &value) \fn template bool QVectorIterator::findNext(const T &value) @@ -839,7 +886,6 @@ \fn template bool QMutableListIterator::findNext(const T &value) \fn template bool QMutableLinkedListIterator::findNext(const T &value) \fn template bool QMutableVectorIterator::findNext(const T &value) - \fn template bool QMutableSetIterator::findNext(const T &value) Searches for \a value starting from the current iterator position forward. Returns \c true if \a value is found; otherwise returns \c false. -- cgit v1.2.3 From 737e7524e8992f508e3f6e57010c81b257c5e994 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Tue, 17 Mar 2020 21:21:24 +0100 Subject: Doc: Fix documentation for deprecated QSet functions QDoc has trouble applying the \obsolete command for multiple topic (\fn) commands in one go. Separate them out and expand the reasoning for deprecation. Mark the rest of the deprecated functions and typedefs related to reverse iterators as \obsolete. Change-Id: I09858efd7e1e5fc890d4f3f063f00c8812fc0b52 Reviewed-by: Lars Knoll Reviewed-by: Paul Wicking --- src/corelib/tools/qset.qdoc | 86 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/src/corelib/tools/qset.qdoc b/src/corelib/tools/qset.qdoc index 33a0697e12..42dd1288ac 100644 --- a/src/corelib/tools/qset.qdoc +++ b/src/corelib/tools/qset.qdoc @@ -399,6 +399,7 @@ */ /*! \fn template QSet::reverse_iterator QSet::rbegin() + \obsolete Deprecated in order to align with std::unordered_set functionality. \since 5.6 Returns a \l{STL-style iterators}{STL-style} reverse iterator pointing to the first @@ -408,11 +409,13 @@ */ /*! \fn template QSet::const_reverse_iterator QSet::rbegin() const + \obsolete Deprecated in order to align with std::unordered_set functionality. \since 5.6 \overload */ /*! \fn template QSet::const_reverse_iterator QSet::crbegin() const + \obsolete Deprecated in order to align with std::unordered_set functionality. \since 5.6 Returns a const \l{STL-style iterators}{STL-style} reverse iterator pointing to the first @@ -422,6 +425,7 @@ */ /*! \fn template QSet::reverse_iterator QSet::rend() + \obsolete Deprecated in order to align with std::unordered_set functionality. \since 5.6 Returns a \l{STL-style iterators}{STL-style} reverse iterator pointing to one past @@ -431,11 +435,13 @@ */ /*! \fn template QSet::const_reverse_iterator QSet::rend() const + \obsolete Deprecated in order to align with std::unordered_set functionality. \since 5.6 \overload */ /*! \fn template QSet::const_reverse_iterator QSet::crend() const + \obsolete Deprecated in order to align with std::unordered_set functionality. \since 5.6 Returns a const \l{STL-style iterators}{STL-style} reverse iterator pointing to one @@ -507,6 +513,7 @@ /*! \typedef QSet::reverse_iterator \since 5.6 + \obsolete Deprecated in order to align with std::unordered_set functionality. The QSet::reverse_iterator typedef provides an STL-style non-const reverse iterator for QSet. @@ -523,6 +530,7 @@ /*! \typedef QSet::const_reverse_iterator \since 5.6 + \obsolete Deprecated in order to align with std::unordered_set functionality. The QSet::const_reverse_iterator typedef provides an STL-style const reverse iterator for QSet. @@ -921,8 +929,20 @@ /*! \fn template QSet::iterator &QSet::iterator::operator--() + \obsolete This operator is deprecated in order to align with std::unordered_set functionality. + + The prefix -- operator (\c{--it}) makes the preceding item + current and returns an iterator to the new current item. + + Calling this function on QSet::begin() leads to undefined + results. + + \sa operator++() +*/ + +/*! \fn template QSet::const_iterator &QSet::const_iterator::operator--() - \obsolete + \obsolete This operator is deprecated in order to align with std::unordered_set functionality. The prefix -- operator (\c{--it}) makes the preceding item current and returns an iterator to the new current item. @@ -935,8 +955,17 @@ /*! \fn template QSet::iterator QSet::iterator::operator--(int) + \obsolete This operator is deprecated in order to align with std::unordered_set functionality. + + \overload + + The postfix -- operator (\c{it--}) makes the preceding item + current and returns an iterator to the previously current item. +*/ + +/*! \fn template QSet::const_iterator QSet::const_iterator::operator--(int) - \obsolete + \obsolete This operator is deprecated in order to align with std::unordered_set functionality. \overload @@ -946,8 +975,19 @@ /*! \fn template QSet::iterator QSet::iterator::operator+(int j) const + \obsolete This operator is deprecated in order to align with std::unordered_set functionality. + + Returns an iterator to the item at \a j positions forward from + this iterator. (If \a j is negative, the iterator goes backward.) + + This operation can be slow for large \a j values. + + \sa operator-() +*/ + +/*! \fn template QSet::const_iterator QSet::const_iterator::operator+(int j) const - \obsolete + \obsolete This operator is deprecated in order to align with std::unordered_set functionality. Returns an iterator to the item at \a j positions forward from this iterator. (If \a j is negative, the iterator goes backward.) @@ -959,8 +999,19 @@ /*! \fn template QSet::iterator QSet::iterator::operator-(int j) const + \obsolete This operator is deprecated in order to align with std::unordered_set functionality. + + Returns an iterator to the item at \a j positions backward from + this iterator. (If \a j is negative, the iterator goes forward.) + + This operation can be slow for large \a j values. + + \sa operator+() +*/ + +/*! \fn template QSet::const_iterator QSet::const_iterator::operator-(int j) const - \obsolete + \obsolete This operator is deprecated in order to align with std::unordered_set functionality. Returns an iterator to the item at \a j positions backward from this iterator. (If \a j is negative, the iterator goes forward.) @@ -972,8 +1023,20 @@ /*! \fn template QSet::iterator &QSet::iterator::operator+=(int j) + \obsolete This operator is deprecated in order to align with std::unordered_set functionality. + + Advances the iterator by \a j items. (If \a j is negative, the + iterator goes backward.) + + This operation can be slow for large \a j values. + + \sa operator-=(), operator+() + +*/ + +/*! \fn template QSet::const_iterator &QSet::const_iterator::operator+=(int j) - \obsolete + \obsolete This operator is deprecated in order to align with std::unordered_set functionality. Advances the iterator by \a j items. (If \a j is negative, the iterator goes backward.) @@ -985,8 +1048,19 @@ /*! \fn template QSet::iterator &QSet::iterator::operator-=(int j) + \obsolete This operator is deprecated in order to align with std::unordered_set functionality. + + Makes the iterator go back by \a j items. (If \a j is negative, + the iterator goes forward.) + + This operation can be slow for large \a j values. + + \sa operator+=(), operator-() +*/ + +/*! \fn template QSet::const_iterator &QSet::const_iterator::operator-=(int j) - \obsolete + \obsolete This operator is deprecated in order to align with std::unordered_set functionality. Makes the iterator go back by \a j items. (If \a j is negative, the iterator goes forward.) -- cgit v1.2.3 From bd9a13b370c65f889da1d42852d8107915edd977 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Tue, 17 Mar 2020 20:50:04 +0100 Subject: Doc: Fix \fn command for qScopeGuard(F &&f) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and tag it in the header file to ensure the documentation is matched. Change-Id: Id1e7cd395ca0ec337845da9a207bfbf95db01064 Reviewed-by: Paul Wicking Reviewed-by: Mårten Nordheim --- src/corelib/tools/qscopeguard.h | 1 + src/corelib/tools/qscopeguard.qdoc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/corelib/tools/qscopeguard.h b/src/corelib/tools/qscopeguard.h index 6a5bc6cc61..d288fd63fc 100644 --- a/src/corelib/tools/qscopeguard.h +++ b/src/corelib/tools/qscopeguard.h @@ -96,6 +96,7 @@ private: template QScopeGuard(F(&)()) -> QScopeGuard; #endif +//! [qScopeGuard] template #if __has_cpp_attribute(nodiscard) Q_REQUIRED_RESULT diff --git a/src/corelib/tools/qscopeguard.qdoc b/src/corelib/tools/qscopeguard.qdoc index 572934d890..b36299d296 100644 --- a/src/corelib/tools/qscopeguard.qdoc +++ b/src/corelib/tools/qscopeguard.qdoc @@ -69,7 +69,7 @@ QT_BEGIN_NAMESPACE */ /*! - \fn template const QScopeGuard qScopeGuard(F f) + \fn [qScopeGuard] template QScopeGuard::type> qScopeGuard(F &&f) \inmodule QtCore \relates QScopeGuard \brief The qScopeGuard function can be used to call a function at the end -- cgit v1.2.3 From f588804725aa1c23c4ddf4903013e727496a4c56 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Tue, 17 Mar 2020 20:36:24 +0100 Subject: Doc: Expand reasoning for QMap deprecations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Idaec1ebd2a392af2b8d778e72acda6bd117d1a48 Reviewed-by: Paul Wicking Reviewed-by: Mårten Nordheim --- src/corelib/tools/qmap.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index 25ea5f25d7..cd139f01d4 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -740,7 +740,7 @@ void QMapDataBase::freeData(QMapDataBase *d) /*! \fn template QList QMap::uniqueKeys() const \since 4.2 - \obsolete + \obsolete Use QMultiMap for storing multiple values with the same key. Returns a list containing all the keys in the map in ascending order. Keys that occur multiple times in the map (because items @@ -802,9 +802,8 @@ void QMapDataBase::freeData(QMapDataBase *d) */ /*! \fn template QList QMap::values(const Key &key) const - \obsolete - \overload + \obsolete Use QMultiMap for maps storing multiple values with the same key. Returns a list containing all the values associated with key \a key, from the most recently inserted to the least recently @@ -1162,7 +1161,7 @@ void QMapDataBase::freeData(QMapDataBase *d) */ /*! \fn template QMap::iterator QMap::insertMulti(const Key &key, const T &value) - \obsolete + \obsolete Use QMultiMap for storing multiple values with the same key. Inserts a new item with the key \a key and a value of \a value. @@ -1177,7 +1176,7 @@ void QMapDataBase::freeData(QMapDataBase *d) /*! \fn template QMap::iterator QMap::insertMulti(const_iterator pos, const Key &key, const T &value) \overload \since 5.1 - \obsolete + \obsolete Use QMultiMap for storing multiple values with the same key. Inserts a new item with the key \a key and value \a value and with hint \a pos suggesting where to do the insert. @@ -1196,7 +1195,7 @@ void QMapDataBase::freeData(QMapDataBase *d) /*! \fn template QMap &QMap::unite(const QMap &other) - \obsolete + \obsolete Use QMultiMap for storing multiple values with the same key. Inserts all the items in the \a other map into this map. If a key is common to both maps, the resulting map will contain the -- cgit v1.2.3 From beffcc590c685b17d86492a3a68852f66b1f4457 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Tue, 17 Mar 2020 19:50:47 +0100 Subject: Doc: Expand reasoning for QHash deprecations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and mark QHash::[const_|key_]iterator operators correctly as deprecated. Change-Id: I01da16254759b9bdb7920709de45a72933d6b5c8 Reviewed-by: Mårten Nordheim Reviewed-by: Paul Wicking --- src/corelib/tools/qhash.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 7ebc374d9f..025c6e9dc0 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -1491,7 +1491,7 @@ uint qHash(long double key, uint seed) noexcept /*! \fn template QList QHash::uniqueKeys() const \since 4.2 - \obsolete + \obsolete Use QMultiHash for storing multiple values with the same key. Returns a list containing all the keys in the map. Keys that occur multiple times in the map (because items were inserted with insertMulti(), or @@ -1537,8 +1537,8 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QList QHash::values(const Key &key) const - \obsolete \overload + \obsolete Use QMultiHash for storing multiple values with the same key. Returns a list of all the values associated with the \a key, from the most recently inserted to the least recently inserted. @@ -2103,6 +2103,7 @@ uint qHash(long double key, uint seed) noexcept /*! \fn template QHash::iterator &QHash::iterator::operator--() + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. The prefix -- operator (\c{--i}) makes the preceding item current and returns an iterator pointing to the new current item. @@ -2115,6 +2116,7 @@ uint qHash(long double key, uint seed) noexcept /*! \fn template QHash::iterator QHash::iterator::operator--(int) + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. \overload @@ -2124,6 +2126,7 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QHash::iterator QHash::iterator::operator+(int j) const + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. Returns an iterator to the item at \a j positions forward from this iterator. (If \a j is negative, the iterator goes backward.) @@ -2135,6 +2138,7 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QHash::iterator QHash::iterator::operator-(int j) const + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. Returns an iterator to the item at \a j positions backward from this iterator. (If \a j is negative, the iterator goes forward.) @@ -2145,6 +2149,7 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QHash::iterator &QHash::iterator::operator+=(int j) + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. Advances the iterator by \a j items. (If \a j is negative, the iterator goes backward.) @@ -2153,6 +2158,7 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QHash::iterator &QHash::iterator::operator-=(int j) + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. Makes the iterator go back by \a j items. (If \a j is negative, the iterator goes forward.) @@ -2296,6 +2302,7 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QHash::const_iterator &QHash::const_iterator::operator--() + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. The prefix -- operator (\c{--i}) makes the preceding item current and returns an iterator pointing to the new current item. @@ -2307,6 +2314,7 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QHash::const_iterator QHash::const_iterator::operator--(int) + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. \overload @@ -2316,6 +2324,7 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QHash::const_iterator QHash::const_iterator::operator+(int j) const + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. Returns an iterator to the item at \a j positions forward from this iterator. (If \a j is negative, the iterator goes backward.) @@ -2326,6 +2335,7 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QHash::const_iterator QHash::const_iterator::operator-(int j) const + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. Returns an iterator to the item at \a j positions backward from this iterator. (If \a j is negative, the iterator goes forward.) @@ -2336,6 +2346,7 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QHash::const_iterator &QHash::const_iterator::operator+=(int j) + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. Advances the iterator by \a j items. (If \a j is negative, the iterator goes backward.) @@ -2346,6 +2357,7 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QHash::const_iterator &QHash::const_iterator::operator-=(int j) + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. Makes the iterator go back by \a j items. (If \a j is negative, the iterator goes forward.) @@ -2440,6 +2452,7 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QHash::key_iterator &QHash::key_iterator::operator--() + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. The prefix -- operator (\c{--i}) makes the preceding item current and returns an iterator pointing to the new current item. @@ -2451,6 +2464,7 @@ uint qHash(long double key, uint seed) noexcept */ /*! \fn template QHash::key_iterator QHash::key_iterator::operator--(int) + \obsolete This operator is deprecated in order to align with std::unordered_map functionality. \overload -- cgit v1.2.3 From 7d0f24b166c0f3e3fc20d89d819e1321f72db57d Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Tue, 17 Mar 2020 19:09:02 +0100 Subject: Doc: Mark QTextStream manipulators as deprecated and instruct to use the ones from the Qt namespace instead. Task-number: QTBUG-82532 Change-Id: I6a85f5096da8aec925a287beff136b77d113926e Reviewed-by: Paul Wicking --- src/corelib/serialization/qtextstream.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/corelib/serialization/qtextstream.cpp b/src/corelib/serialization/qtextstream.cpp index b8137e0abd..ce4e343d20 100644 --- a/src/corelib/serialization/qtextstream.cpp +++ b/src/corelib/serialization/qtextstream.cpp @@ -2694,6 +2694,7 @@ namespace Qt { /*! \relates QTextStream + \obsolete Use Qt::bin() instead. Calls QTextStream::setIntegerBase(2) on \a stream and returns \a stream. @@ -2708,6 +2709,7 @@ QTextStream &bin(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::oct() instead. Calls QTextStream::setIntegerBase(8) on \a stream and returns \a stream. @@ -2722,6 +2724,7 @@ QTextStream &oct(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::dec() instead. Calls QTextStream::setIntegerBase(10) on \a stream and returns \a stream. @@ -2736,6 +2739,7 @@ QTextStream &dec(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::hex() instead. Calls QTextStream::setIntegerBase(16) on \a stream and returns \a stream. @@ -2751,6 +2755,7 @@ QTextStream &hex(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::showbase() instead. Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | QTextStream::ShowBase) on \a stream and returns \a stream. @@ -2765,6 +2770,7 @@ QTextStream &showbase(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::forcesign() instead. Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | QTextStream::ForceSign) on \a stream and returns \a stream. @@ -2779,6 +2785,7 @@ QTextStream &forcesign(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::forcepoint() instead. Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | QTextStream::ForcePoint) on \a stream and returns \a stream. @@ -2793,6 +2800,7 @@ QTextStream &forcepoint(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::noshowbase() instead. Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & ~QTextStream::ShowBase) on \a stream and returns \a stream. @@ -2807,6 +2815,7 @@ QTextStream &noshowbase(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::noforcesign() instead. Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & ~QTextStream::ForceSign) on \a stream and returns \a stream. @@ -2821,6 +2830,7 @@ QTextStream &noforcesign(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::noforcepoint() instead. Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & ~QTextStream::ForcePoint) on \a stream and returns \a stream. @@ -2835,6 +2845,7 @@ QTextStream &noforcepoint(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::uppercasebase() instead. Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | QTextStream::UppercaseBase) on \a stream and returns \a stream. @@ -2849,6 +2860,7 @@ QTextStream &uppercasebase(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::uppercasedigits() instead. Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | QTextStream::UppercaseDigits) on \a stream and returns \a stream. @@ -2863,6 +2875,7 @@ QTextStream &uppercasedigits(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::lowercasebase() instead. Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & ~QTextStream::UppercaseBase) on \a stream and returns \a stream. @@ -2877,6 +2890,7 @@ QTextStream &lowercasebase(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::lowercasedigits() instead. Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & ~QTextStream::UppercaseDigits) on \a stream and returns \a stream. @@ -2891,6 +2905,7 @@ QTextStream &lowercasedigits(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::fixed() instead. Calls QTextStream::setRealNumberNotation(QTextStream::FixedNotation) on \a stream and returns \a stream. @@ -2905,6 +2920,7 @@ QTextStream &fixed(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::scientific() instead. Calls QTextStream::setRealNumberNotation(QTextStream::ScientificNotation) on \a stream and returns \a stream. @@ -2919,6 +2935,7 @@ QTextStream &scientific(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::left() instead. Calls QTextStream::setFieldAlignment(QTextStream::AlignLeft) on \a stream and returns \a stream. @@ -2933,6 +2950,7 @@ QTextStream &left(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::right() instead. Calls QTextStream::setFieldAlignment(QTextStream::AlignRight) on \a stream and returns \a stream. @@ -2947,6 +2965,7 @@ QTextStream &right(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::center() instead. Calls QTextStream::setFieldAlignment(QTextStream::AlignCenter) on \a stream and returns \a stream. @@ -2961,6 +2980,7 @@ QTextStream ¢er(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::endl() instead. Writes '\\n' to the \a stream and flushes the stream. @@ -2980,6 +3000,7 @@ QTextStream &endl(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::flush() instead. Calls QTextStream::flush() on \a stream and returns \a stream. @@ -2993,6 +3014,7 @@ QTextStream &flush(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::reset() instead. Calls QTextStream::reset() on \a stream and returns \a stream. @@ -3006,6 +3028,7 @@ QTextStream &reset(QTextStream &stream) /*! \relates QTextStream + \obsolete Use Qt::ws() instead. Calls \l {QTextStream::}{skipWhiteSpace()} on \a stream and returns \a stream. @@ -3045,6 +3068,7 @@ QTextStream &ws(QTextStream &stream) namespace Qt { /*! \relates QTextStream + \obsolete Use Qt::bom() instead. Toggles insertion of the Byte Order Mark on \a stream when QTextStream is used with a UTF codec. -- cgit v1.2.3 From 07558ae12d9b86bae9c5ee2b04414e6e34ec61b3 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Tue, 17 Mar 2020 17:27:08 +0100 Subject: Doc: QItemSelectionModel: Note when function default arguments were added Change-Id: I4b7a9269b68c86548035b57211c4c1b5ad451767 Reviewed-by: Paul Wicking --- src/corelib/itemmodels/qitemselectionmodel.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/corelib/itemmodels/qitemselectionmodel.cpp b/src/corelib/itemmodels/qitemselectionmodel.cpp index e4ac5da299..27442fb2fa 100644 --- a/src/corelib/itemmodels/qitemselectionmodel.cpp +++ b/src/corelib/itemmodels/qitemselectionmodel.cpp @@ -1473,6 +1473,9 @@ bool QItemSelectionModel::isSelected(const QModelIndex &index) const Note that this function is usually faster than calling isSelected() on all items in the same row and that unselectable items are ignored. + + \note Since Qt 5.15, the default argument for \a parent is an empty + model index. */ bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) const { @@ -1545,6 +1548,9 @@ bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) cons Note that this function is usually faster than calling isSelected() on all items in the same column and that unselectable items are ignored. + + \note Since Qt 5.15, the default argument for \a parent is an empty + model index. */ bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent) const { @@ -1616,6 +1622,9 @@ bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent /*! Returns \c true if there are any items selected in the \a row with the given \a parent. + + \note Since Qt 5.15, the default argument for \a parent is an empty + model index. */ bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &parent) const { @@ -1649,6 +1658,9 @@ bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &par /*! Returns \c true if there are any items selected in the \a column with the given \a parent. + + \note Since Qt 5.15, the default argument for \a parent is an empty + model index. */ bool QItemSelectionModel::columnIntersectsSelection(int column, const QModelIndex &parent) const { -- cgit v1.2.3 From 0b5a1100ff5ab4fb696e0efc9bc5e8f266cf40ae Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Mon, 23 Mar 2020 11:08:23 +0100 Subject: Doc: Adjust offline style for the litehtml rendering engine Qt Creator now has the option to use the litehtml backend as a replacement for the QTextBrowser-based help viewer. Fix a few issues that the offline style has with the litehtml renderer: - Fall back to using an unordered list for the navigation bar. - Remove the background image for #buildversion and adjust font size. - Adjust the generated padding around code snippets. Keep the script that switches the offline-simple.css style to the full offline style (offline.css) for JavaScript-enabled browsers. The litehtml backend in Qt Creator will handle this switch internally. With these changes, the generated offline documentation looks acceptable when rendered with QTextBrowser, litehtml, or desktop web browsers. Fixes: QTBUG-82567 Change-Id: I86b179b1985b7ef54feddab30cb227b28021efe5 Reviewed-by: Leena Miettinen --- .../qt-html-templates-offline-simple.qdocconf | 22 +++------------------- doc/global/template/style/offline.css | 5 ++++- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/doc/global/qt-html-templates-offline-simple.qdocconf b/doc/global/qt-html-templates-offline-simple.qdocconf index c3c2d3cca0..02a3228519 100644 --- a/doc/global/qt-html-templates-offline-simple.qdocconf +++ b/doc/global/qt-html-templates-offline-simple.qdocconf @@ -19,27 +19,11 @@ HTML.headerstyles = \ " }, 0);\n" \ " \n" -HTML.postheader = \ - "\n" \ - "
\n"\ - "
\n" \ - "
\n" \ - "
\n" \ - " \n" - -HTML.postpostheader = \ - "
\n"\ - "
\n" \ - "
\n" \ - "
\n" \ - "
\n" \ - "
\n" - HTML.navigationseparator = \ "\n" # Add some padding around code snippets, as we cannot # currectly style them for QTextBrowser using only CSS. -codeindent = 2 -codeprefix = "\n\n" -codesuffix = "\n\n" +codeindent = 1 +codeprefix = "\n" +codesuffix = "\n" diff --git a/doc/global/template/style/offline.css b/doc/global/template/style/offline.css index 13c942707a..f99369a096 100644 --- a/doc/global/template/style/offline.css +++ b/doc/global/template/style/offline.css @@ -297,11 +297,14 @@ table.buildversion { #buildversion { font-style: italic; - font-size: small; float: right; margin-right: 5px; } +#buildversion a { + background: none; +} + /* /* table of content -- cgit v1.2.3 From 96de59d7c1068022dbb590d7abbdcf6eaf5d0566 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Tue, 17 Mar 2020 17:33:07 +0100 Subject: Doc: Document new property change notifiers for QSortFilterProxyModel Change-Id: I02789fa2281d33c7344a5d5e730a27fe6fa50e56 Reviewed-by: Paul Wicking --- src/corelib/itemmodels/qsortfilterproxymodel.cpp | 48 ++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/src/corelib/itemmodels/qsortfilterproxymodel.cpp index 122b95ed57..35c97da532 100644 --- a/src/corelib/itemmodels/qsortfilterproxymodel.cpp +++ b/src/corelib/itemmodels/qsortfilterproxymodel.cpp @@ -2663,12 +2663,19 @@ void QSortFilterProxyModel::setFilterKeyColumn(int column) \property QSortFilterProxyModel::filterCaseSensitivity \brief the case sensitivity of the QRegExp pattern used to filter the - contents of the source model + contents of the source model. By default, the filter is case sensitive. \sa filterRegExp, sortCaseSensitivity */ + +/*! + \since 5.15 + \fn void QSortFilterProxyModel::filterCaseSensitivityChanged(Qt::CaseSensitivity filterCaseSensitivity) + \brief This signal is emitted when the case sensitivity of the filter + changes to \a filterCaseSensitivity. + */ Qt::CaseSensitivity QSortFilterProxyModel::filterCaseSensitivity() const { Q_D(const QSortFilterProxyModel); @@ -2695,6 +2702,13 @@ void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs) \sa filterCaseSensitivity, lessThan() */ + +/*! + \since 5.15 + \fn void QSortFilterProxyModel::sortCaseSensitivityChanged(Qt::CaseSensitivity sortCaseSensitivity) + \brief This signal is emitted when the case sensitivity for sorting + changes to \a sortCaseSensitivity. +*/ Qt::CaseSensitivity QSortFilterProxyModel::sortCaseSensitivity() const { Q_D(const QSortFilterProxyModel); @@ -2721,6 +2735,13 @@ void QSortFilterProxyModel::setSortCaseSensitivity(Qt::CaseSensitivity cs) \sa sortCaseSensitivity, lessThan() */ + +/*! + \since 5.15 + \fn void QSortFilterProxyModel::sortLocaleAwareChanged(bool sortLocaleAware) + \brief This signal is emitted when the locale aware setting + changes to \a sortLocaleAware. +*/ bool QSortFilterProxyModel::isSortLocaleAware() const { Q_D(const QSortFilterProxyModel); @@ -2841,12 +2862,19 @@ void QSortFilterProxyModel::setDynamicSortFilter(bool enable) /*! \since 4.2 \property QSortFilterProxyModel::sortRole - \brief the item role that is used to query the source model's data when sorting items + \brief the item role that is used to query the source model's data when + sorting items. The default value is Qt::DisplayRole. \sa lessThan() */ + +/*! + \since 5.15 + \fn void QSortFilterProxyModel::sortRoleChanged(int sortRole) + \brief This signal is emitted when the sort role changes to \a sortRole. +*/ int QSortFilterProxyModel::sortRole() const { Q_D(const QSortFilterProxyModel); @@ -2866,12 +2894,19 @@ void QSortFilterProxyModel::setSortRole(int role) /*! \since 4.2 \property QSortFilterProxyModel::filterRole - \brief the item role that is used to query the source model's data when filtering items + \brief the item role that is used to query the source model's data when + filtering items. The default value is Qt::DisplayRole. \sa filterAcceptsRow() */ + +/*! + \since 5.15 + \fn void QSortFilterProxyModel::filterRoleChanged(int filterRole) + \brief This signal is emitted when the filter role changes to \a filterRole. +*/ int QSortFilterProxyModel::filterRole() const { Q_D(const QSortFilterProxyModel); @@ -2899,6 +2934,13 @@ void QSortFilterProxyModel::setFilterRole(int role) \sa filterAcceptsRow() */ + +/*! + \since 5.15 + \fn void QSortFilterProxyModel::recursiveFilteringEnabledChanged(int recursiveFilteringEnabled) + \brief This signal is emitted when the recursive filter setting is changed + to \a recursiveFilteringEnabled. +*/ bool QSortFilterProxyModel::isRecursiveFilteringEnabled() const { Q_D(const QSortFilterProxyModel); -- cgit v1.2.3 From 643410136004825cb395e342f5c4ff90e85de4fe Mon Sep 17 00:00:00 2001 From: Lorn Potter Date: Sat, 7 Sep 2019 07:06:30 +1000 Subject: wasm: refactor network to use fetch API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This has better support for threaded use, and gets rid of bind use. This requires emscripten 1.38.37 and above Task-number: QTBUG-76891 Change-Id: Ic30a6820c2ce945c314751c06cfc356914a71217 Reviewed-by: Morten Johan Sørvig --- mkspecs/wasm-emscripten/qmake.conf | 3 +- src/network/access/qnetworkaccessmanager.cpp | 23 +- src/network/access/qnetworkreplywasmimpl.cpp | 311 ++++++++++----------------- src/network/access/qnetworkreplywasmimpl_p.h | 14 +- 4 files changed, 134 insertions(+), 217 deletions(-) diff --git a/mkspecs/wasm-emscripten/qmake.conf b/mkspecs/wasm-emscripten/qmake.conf index e6a9773482..a1b11e93aa 100644 --- a/mkspecs/wasm-emscripten/qmake.conf +++ b/mkspecs/wasm-emscripten/qmake.conf @@ -37,7 +37,8 @@ EMCC_COMMON_LFLAGS += \ -s NO_EXIT_RUNTIME=0 \ -s ERROR_ON_UNDEFINED_SYMBOLS=1 \ -s EXTRA_EXPORTED_RUNTIME_METHODS=[\"UTF16ToString\",\"stringToUTF16\"] \ - --bind + --bind \ + -s FETCH=1 # The -s arguments can also be used with release builds, # but are here in debug for clarity. diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index e7e6e6b1a4..9e0389e08d 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -1410,16 +1410,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera bool isLocalFile = req.url().isLocalFile(); QString scheme = req.url().scheme(); -#ifdef Q_OS_WASM - // Support http, https, and relateive urls - if (scheme == QLatin1String("http") || scheme == QLatin1String("https") || scheme.isEmpty()) { - QNetworkReplyWasmImpl *reply = new QNetworkReplyWasmImpl(this); - QNetworkReplyWasmImplPrivate *priv = reply->d_func(); - priv->manager = this; - priv->setup(op, req, outgoingData); - return reply; - } -#endif +#ifndef Q_OS_WASM // fast path for GET on file:// URLs // The QNetworkAccessFileBackend will right now only be used for PUT @@ -1506,7 +1497,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera } #endif } - +#endif QNetworkRequest request = req; if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() && outgoingData && !outgoingData->isSequential()) { @@ -1524,6 +1515,16 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies)); } } +#ifdef Q_OS_WASM + // Support http, https, and relative urls + if (scheme == QLatin1String("http") || scheme == QLatin1String("https") || scheme.isEmpty()) { + QNetworkReplyWasmImpl *reply = new QNetworkReplyWasmImpl(this); + QNetworkReplyWasmImplPrivate *priv = reply->d_func(); + priv->manager = this; + priv->setup(op, request, outgoingData); + return reply; + } +#endif #if QT_CONFIG(http) // Since Qt 5 we use the new QNetworkReplyHttpImpl diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp index 8c0f9bdf55..af8b39bab6 100644 --- a/src/network/access/qnetworkreplywasmimpl.cpp +++ b/src/network/access/qnetworkreplywasmimpl.cpp @@ -50,180 +50,10 @@ #include #include -#include -#include +#include QT_BEGIN_NAMESPACE -using namespace emscripten; - -static void q_requestErrorCallback(val event) -{ - if (event.isNull() || event.isUndefined()) - return; - - val xhr = event["target"]; - if (xhr.isNull() || xhr.isUndefined()) - return; - - quintptr func = xhr["data-handler"].as(); - QNetworkReplyWasmImplPrivate *reply = reinterpret_cast(func); - Q_ASSERT(reply); - - int statusCode = xhr["status"].as(); - - QString reasonStr = QString::fromStdString(xhr["statusText"].as()); - - reply->setReplyAttributes(func, statusCode, reasonStr); - - if (statusCode >= 400 && !reasonStr.isEmpty()) - reply->emitReplyError(reply->statusCodeFromHttp(statusCode, reply->request.url()), reasonStr); -} - -static void q_progressCallback(val event) -{ - if (event.isNull() || event.isUndefined()) - return; - - val xhr = event["target"]; - if (xhr.isNull() || xhr.isUndefined()) - return; - - QNetworkReplyWasmImplPrivate *reply = - reinterpret_cast(xhr["data-handler"].as()); - Q_ASSERT(reply); - - if (xhr["status"].as() < 400) - reply->emitDataReadProgress(event["loaded"].as(), event["total"].as()); -} - -static void q_loadCallback(val event) -{ - if (event.isNull() || event.isUndefined()) - return; - - val xhr = event["target"]; - if (xhr.isNull() || xhr.isUndefined()) - return; - - QNetworkReplyWasmImplPrivate *reply = - reinterpret_cast(xhr["data-handler"].as()); - Q_ASSERT(reply); - - int status = xhr["status"].as(); - if (status >= 300) { - q_requestErrorCallback(event); - return; - } - QString statusText = QString::fromStdString(xhr["statusText"].as()); - int readyState = xhr["readyState"].as(); - - if (status == 200 || status == 203) { - QString responseString; - const std::string responseType = xhr["responseType"].as(); - if (responseType.length() == 0 || responseType == "document" || responseType == "text") { - responseString = QString::fromStdWString(xhr["responseText"].as()); - } else if (responseType == "json") { - responseString = - QString::fromStdWString(val::global("JSON").call("stringify", xhr["response"])); - } else if (responseType == "arraybuffer" || responseType == "blob") { - // handle this data in the FileReader, triggered by the call to readAsArrayBuffer - val blob = xhr["response"]; - if (blob.isNull() || blob.isUndefined()) - return; - - val reader = val::global("FileReader").new_(); - if (reader.isNull() || reader.isUndefined()) - return; - - reader.set("onload", val::module_property("qt_QNetworkReplyWasmImplPrivate_readBinary")); - reader.set("data-handler", xhr["data-handler"]); - - reader.call("readAsArrayBuffer", blob); - val::global("Module").delete_(reader); - } - - - if (readyState == 4) { // done - reply->setReplyAttributes(xhr["data-handler"].as(), status, statusText); - if (!responseString.isEmpty()) { - QByteArray responseStringArray = responseString.toUtf8(); - reply->dataReceived(responseStringArray, responseStringArray.size()); - } - } - } - if (status >= 400 && !statusText.isEmpty()) - reply->emitReplyError(reply->statusCodeFromHttp(status, reply->request.url()), statusText); -} - -static void q_responseHeadersCallback(val event) -{ - if (event.isNull() || event.isUndefined()) - return; - - val xhr = event["target"]; - if (xhr.isNull() || xhr.isUndefined()) - return; - - if (xhr["readyState"].as() == 2) { // HEADERS_RECEIVED - std::string responseHeaders = xhr.call("getAllResponseHeaders"); - if (!responseHeaders.empty()) { - QNetworkReplyWasmImplPrivate *reply = - reinterpret_cast(xhr["data-handler"].as()); - Q_ASSERT(reply); - - reply->headersReceived(QString::fromStdString(responseHeaders)); - } - } -} - -static void q_readBinary(val event) -{ - if (event.isNull() || event.isUndefined()) - return; - - val fileReader = event["target"]; - if (fileReader.isNull() || fileReader.isUndefined()) - return; - - QNetworkReplyWasmImplPrivate *reply = - reinterpret_cast(fileReader["data-handler"].as()); - Q_ASSERT(reply); - - if (reply->state == QNetworkReplyPrivate::Finished || reply->state == QNetworkReplyPrivate::Aborted) - return; - - // Set up source typed array - val result = fileReader["result"]; // ArrayBuffer - if (result.isNull() || result.isUndefined()) - return; - - val Uint8Array = val::global("Uint8Array"); - val sourceTypedArray = Uint8Array.new_(result); - - // Allocate and set up destination typed array - const quintptr size = result["byteLength"].as(); - QByteArray buffer(size, Qt::Uninitialized); - - val destinationTypedArray = Uint8Array.new_(val::module_property("HEAPU8")["buffer"], - reinterpret_cast(buffer.data()), size); - destinationTypedArray.call("set", sourceTypedArray); - reply->dataReceived(buffer, buffer.size()); - - event.delete_(fileReader); - Uint8Array.delete_(sourceTypedArray); - - QCoreApplication::processEvents(); -} - -EMSCRIPTEN_BINDINGS(qtNetworkModule) { - function("qt_QNetworkReplyWasmImplPrivate_requestErrorCallback", q_requestErrorCallback); - function("qt_QNetworkReplyWasmImplPrivate_progressCallback", q_progressCallback); - function("qt_QNetworkReplyWasmImplPrivate_loadCallback", q_loadCallback); - function("qt_QNetworkReplyWasmImplPrivate_responseHeadersCallback", q_responseHeadersCallback); - function("qt_QNetworkReplyWasmImplPrivate_readBinary", q_readBinary); -} - QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate() : QNetworkReplyPrivate() , managerPrivate(0) @@ -236,11 +66,7 @@ QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate() QNetworkReplyWasmImplPrivate::~QNetworkReplyWasmImplPrivate() { - m_xhr.set("onerror", val::null()); - m_xhr.set("onload", val::null()); - m_xhr.set("onprogress", val::null()); - m_xhr.set("onreadystatechange", val::null()); - m_xhr.set("data-handler", val::null()); + emscripten_fetch_close(m_fetch); } QNetworkReplyWasmImpl::QNetworkReplyWasmImpl(QObject *parent) @@ -373,7 +199,11 @@ void QNetworkReplyWasmImplPrivate::setReplyAttributes(quintptr data, int statusC void QNetworkReplyWasmImplPrivate::doAbort() const { - m_xhr.call("abort"); + emscripten_fetch_close(m_fetch); +} + +constexpr int getArraySize (int factor) { + return 2 * factor + 1; } void QNetworkReplyWasmImplPrivate::doSendRequest() @@ -381,38 +211,68 @@ void QNetworkReplyWasmImplPrivate::doSendRequest() Q_Q(QNetworkReplyWasmImpl); totalDownloadSize = 0; - m_xhr = val::global("XMLHttpRequest").new_(); - std::string verb = q->methodName().toStdString(); + emscripten_fetch_attr_t attr; + emscripten_fetch_attr_init(&attr); + strcpy(attr.requestMethod, q->methodName().constData()); - m_xhr.call("open", verb, request.url().toString().toStdString()); + QList headersData = request.rawHeaderList(); + int arrayLength = getArraySize(headersData.count()); - m_xhr.set("onerror", val::module_property("qt_QNetworkReplyWasmImplPrivate_requestErrorCallback")); - m_xhr.set("onload", val::module_property("qt_QNetworkReplyWasmImplPrivate_loadCallback")); - m_xhr.set("onprogress", val::module_property("qt_QNetworkReplyWasmImplPrivate_progressCallback")); - m_xhr.set("onreadystatechange", val::module_property("qt_QNetworkReplyWasmImplPrivate_responseHeadersCallback")); + if (headersData.count() > 0) { + const char* customHeaders[arrayLength]; + int i = 0; + for (i; i < headersData.count() * 2; (i = i + 2)) { + customHeaders[i] = headersData[i].constData(); + customHeaders[i + 1] = request.rawHeader(headersData[i]).constData(); + } + customHeaders[i] = nullptr; + attr.requestHeaders = customHeaders; + } - m_xhr.set("data-handler", val(quintptr(reinterpret_cast(this)))); + if (outgoingData) { // data from post request + // handle extra data + QByteArray extraData; + extraData = outgoingData->readAll(); // is there a size restriction here? + if (!extraData.isEmpty()) { + attr.requestData = extraData.constData(); + attr.requestDataSize = extraData.size(); + } + } - QByteArray contentType = request.rawHeader("Content-Type"); + // username & password + if (!request.url().userInfo().isEmpty()) { + attr.userName = request.url().userName().toUtf8(); + attr.password = request.url().password().toUtf8(); + } - // handle extra data - val dataToSend = val::null(); - QByteArray extraData; + attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY | EMSCRIPTEN_FETCH_PERSIST_FILE | EMSCRIPTEN_FETCH_REPLACE; - if (outgoingData) // data from post request - extraData = outgoingData->readAll(); + QNetworkRequest::CacheLoadControl CacheLoadControlAttribute = + (QNetworkRequest::CacheLoadControl)request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(); - if (!extraData.isEmpty()) { - dataToSend = val(typed_memory_view(extraData.size(), - reinterpret_cast - (extraData.constData()))); + if (CacheLoadControlAttribute == QNetworkRequest::AlwaysCache) { + attr.attributes += EMSCRIPTEN_FETCH_NO_DOWNLOAD; } - m_xhr.set("responseType", val("blob")); - // set request headers - for (auto header : request.rawHeaderList()) { - m_xhr.call("setRequestHeader", header.toStdString(), request.rawHeader(header).toStdString()); + if (CacheLoadControlAttribute == QNetworkRequest::PreferCache) { + attr.attributes += EMSCRIPTEN_FETCH_APPEND; } - m_xhr.call("send", dataToSend); + + if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork || + request.attribute(QNetworkRequest::CacheSaveControlAttribute, false).toBool()) { + attr.attributes -= EMSCRIPTEN_FETCH_PERSIST_FILE; + } + + attr.onsuccess = QNetworkReplyWasmImplPrivate::downloadSucceeded; + attr.onerror = QNetworkReplyWasmImplPrivate::downloadFailed; + attr.onprogress = QNetworkReplyWasmImplPrivate::downloadProgress; + attr.onreadystatechange = QNetworkReplyWasmImplPrivate::stateChange; + attr.timeoutMSecs = 2 * 6000; // FIXME + attr.userData = reinterpret_cast(this); + + QString dPath = QStringLiteral("/home/web_user/") + request.url().fileName(); + attr.destinationPath = dPath.toUtf8(); + + m_fetch = emscripten_fetch(&attr, request.url().toString().toUtf8()); } void QNetworkReplyWasmImplPrivate::emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString) @@ -511,7 +371,6 @@ void QNetworkReplyWasmImplPrivate::headersReceived(const QString &bufferString) if (!bufferString.isEmpty()) { QStringList headers = bufferString.split(QString::fromUtf8("\r\n"), Qt::SkipEmptyParts); - for (int i = 0; i < headers.size(); i++) { QString headerName = headers.at(i).split(QString::fromUtf8(": ")).at(0); QString headersValue = headers.at(i).split(QString::fromUtf8(": ")).at(1); @@ -589,6 +448,56 @@ void QNetworkReplyWasmImplPrivate::_q_bufferOutgoingData() } } +void QNetworkReplyWasmImplPrivate::downloadSucceeded(emscripten_fetch_t *fetch) +{ + QByteArray buffer(fetch->data, fetch->numBytes); + + QNetworkReplyWasmImplPrivate *reply = + reinterpret_cast(fetch->userData); + if (reply) { + reply->dataReceived(buffer, buffer.size()); + } +} + +void QNetworkReplyWasmImplPrivate::stateChange(emscripten_fetch_t *fetch) +{ + if (fetch->readyState == /*HEADERS_RECEIVED*/ 2) { + size_t headerLength = emscripten_fetch_get_response_headers_length(fetch); + char *dst = nullptr; + emscripten_fetch_get_response_headers(fetch, dst, headerLength + 1); + std::string str = dst; + QNetworkReplyWasmImplPrivate *reply = + reinterpret_cast(fetch->userData); + reply->headersReceived(QString::fromStdString(str)); + } +} + +void QNetworkReplyWasmImplPrivate::downloadProgress(emscripten_fetch_t *fetch) +{ + QNetworkReplyWasmImplPrivate *reply = + reinterpret_cast(fetch->userData); + Q_ASSERT(reply); + + if (fetch->status < 400) + reply->emitDataReadProgress((fetch->dataOffset + fetch->numBytes), fetch->totalBytes); +} + +void QNetworkReplyWasmImplPrivate::downloadFailed(emscripten_fetch_t *fetch) +{ + QNetworkReplyWasmImplPrivate *reply = reinterpret_cast(fetch->userData); + Q_ASSERT(reply); + + QString reasonStr = QString::fromUtf8(fetch->statusText); + + reply->setReplyAttributes(reinterpret_cast(fetch->userData), fetch->status, reasonStr); + + if (fetch->status >= 400 && !reasonStr.isEmpty()) + reply->emitReplyError(reply->statusCodeFromHttp(fetch->status, reply->request.url()), reasonStr); + + if (fetch->status >= 400) + emscripten_fetch_close(fetch); // Also free data on failure. +} + //taken from qhttpthreaddelegate.cpp QNetworkReply::NetworkError QNetworkReplyWasmImplPrivate::statusCodeFromHttp(int httpStatusCode, const QUrl &url) { diff --git a/src/network/access/qnetworkreplywasmimpl_p.h b/src/network/access/qnetworkreplywasmimpl_p.h index e1e6bf4e24..5463ce8a98 100644 --- a/src/network/access/qnetworkreplywasmimpl_p.h +++ b/src/network/access/qnetworkreplywasmimpl_p.h @@ -61,8 +61,7 @@ #include #include -#include -#include +#include QT_BEGIN_NAMESPACE @@ -135,10 +134,17 @@ public: QIODevice *outgoingData; QSharedPointer outgoingDataBuffer; - emscripten::val m_xhr = emscripten::val::null(); - void doAbort() const; + void doAbort() const; + + static void downloadProgress(emscripten_fetch_t *fetch); + static void downloadFailed(emscripten_fetch_t *fetch); + static void downloadSucceeded(emscripten_fetch_t *fetch); + static void stateChange(emscripten_fetch_t *fetch); static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url); + + emscripten_fetch_t *m_fetch; + Q_DECLARE_PUBLIC(QNetworkReplyWasmImpl) }; -- cgit v1.2.3 From d4a4202caaf1676e8ae49359594d60895ff45d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 20 Mar 2020 17:27:33 +0100 Subject: widgets: Sync client rect of paint-on-screen widgets during resize Paint events are delivered with the client rect of the widget, and this applies to paint-on-screen widgets as well. The same goes for how the widget repaint manager tracks dirty rects. Internally we were also calling paintOnScreen() with client rects, so the use of geometry() in the resize handler was likely a bug/oversight. Change-Id: I1312ccf77218d1162e0971e4cbabaa80f49c852c Reviewed-by: Volker Hilsheimer --- src/widgets/kernel/qwidgetwindow.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 67b7d05499..0239856ed9 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -813,16 +813,16 @@ void QWidgetWindow::handleMoveEvent(QMoveEvent *event) void QWidgetWindow::handleResizeEvent(QResizeEvent *event) { - QSize oldSize = m_widget->data->crect.size(); + auto oldRect = m_widget->rect(); if (updateSize()) { QGuiApplication::forwardEvent(m_widget, event); if (m_widget->d_func()->shouldPaintOnScreen()) { - QRegion updateRegion(geometry()); + QRegion dirtyRegion = m_widget->rect(); if (m_widget->testAttribute(Qt::WA_StaticContents)) - updateRegion -= QRect(0, 0, oldSize.width(), oldSize.height()); - m_widget->d_func()->syncBackingStore(updateRegion); + dirtyRegion -= oldRect; + m_widget->d_func()->syncBackingStore(dirtyRegion); } else { m_widget->d_func()->syncBackingStore(); } -- cgit v1.2.3 From 0613bf5d160e79476435da54f2cad2676de4a558 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Thu, 26 Mar 2020 17:30:47 +0100 Subject: QTextEdit: remove the cursor when a read-only textedit looses focus A read-only text edit with Qt::TextSelectableByKeyboard shows a steady cursor to indicate to users that they can select the text, but not edit it. When the control receives focus, it doesn't turn on blinking, but explicitly sets cursorOn to true. When focus is lost, then cursorOn needs to be reset to false to make the cursor disappear, even if the blinking (as indicated by the poorly named cursorVisible variable) is not on. Change-Id: I78408b5c50c6ede3f9a7128be7a31b9c6795cf9c Fixes: QTBUG-83029 Reviewed-by: Richard Moe Gustavsen --- src/widgets/widgets/qwidgettextcontrol.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index 45ab384036..2e302e3d9c 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -2236,6 +2236,7 @@ void QWidgetTextControlPrivate::focusEvent(QFocusEvent *e) #endif } else { setCursorVisible(false); + cursorOn = false; if (cursorIsFocusIndicator && e->reason() != Qt::ActiveWindowFocusReason -- cgit v1.2.3 From 533f7d7ca328e81804c8f52818f9d39172694f94 Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Fri, 27 Mar 2020 09:41:03 +0100 Subject: Raster painting: fix dashing for separate lines When drawing multiple distinct (unconnected) lines (e.g. from QPainter::drawLines() or a QPainterPath with alternating movetos/linetos), the dash pattern should not continue from one to the next, as it should when drawing a connected line (e.g. polyline). Both the cosmetic stroker and the full stroker does it right, but the fast rasterizing codepath got it wrong. Fixes: QTBUG-83048 Change-Id: I3d090f7121726755a0e53cb66b99a5563ac0e1c0 Reviewed-by: Allan Sandfeld Jensen --- src/gui/painting/qpaintengine_raster.cpp | 7 +++-- tests/auto/other/lancelot/scripts/linedashes.qps | 40 +++++++++++++++++++++++- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 10920c38fe..5b7f8511ba 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -1713,8 +1713,11 @@ void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen) width / line.length(), s->lastPen.capStyle() == Qt::SquareCap); } else { - d->rasterizeLine_dashed(line, width, - &dashIndex, &dashOffset, &inDash); + // LinesHint means each line is distinct, so restart dashing + int dIndex = dashIndex; + qreal dOffset = dashOffset; + bool inD = inDash; + d->rasterizeLine_dashed(line, width, &dIndex, &dOffset, &inD); } } } diff --git a/tests/auto/other/lancelot/scripts/linedashes.qps b/tests/auto/other/lancelot/scripts/linedashes.qps index ee7d18b156..78c791e68b 100644 --- a/tests/auto/other/lancelot/scripts/linedashes.qps +++ b/tests/auto/other/lancelot/scripts/linedashes.qps @@ -91,4 +91,42 @@ translate 100 0 repeat_block draw_lines setPen 0xffff0000 0 dashline squarecap translate 100 0 -repeat_block draw_lines \ No newline at end of file +repeat_block draw_lines + +path_moveTo mypath 10 10 +path_lineTo mypath 87 10 +path_moveTo mypath 10 30 +path_lineTo mypath 87 30 +path_moveTo mypath 10 50 +path_lineTo mypath 87 50 + +resetMatrix +translate 0 150 + +begin_block distinctLines + +setPen black 0 SolidLine SquareCap +pen_setDashPattern [ 3 3 ] +drawPath mypath + +translate 100 0 +setPen black 5 SolidLine SquareCap +pen_setDashPattern [ 3 3 ] +drawPath mypath + +translate 100 0 +setPen black 0 SolidLine RoundCap +pen_setDashPattern [ 3 3 ] +drawPath mypath + +translate 100 0 +setPen black 5 SolidLine RoundCap +pen_setDashPattern [ 3 3 ] +drawPath mypath + +end_block distinctLines + +resetMatrix +translate 0 220 +setRenderHint Antialiasing true +repeat_block distinctLines -- cgit v1.2.3 From 0b4ae8e6829e32b2648e66c010c325e9c9a0231c Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Thu, 26 Mar 2020 14:23:21 +0100 Subject: MinGW: Fix build with -angle Since Qt 5.14, debug libs for MinGW don't necessarily have a 'd' suffix anymore. Fixes: QTBUG-83087 Change-Id: Ia9f499ebed05e96fb056134681a2124c2262fb08 Reviewed-by: Joerg Bornemann --- mkspecs/features/win32/opengl.prf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mkspecs/features/win32/opengl.prf b/mkspecs/features/win32/opengl.prf index f21848f941..8568a0c379 100644 --- a/mkspecs/features/win32/opengl.prf +++ b/mkspecs/features/win32/opengl.prf @@ -3,8 +3,9 @@ QT_FOR_CONFIG += gui defineTest(prependOpenGlLib) { path = $$QT.core.libs/$$QMAKE_PREFIX_STATICLIB$$1 ext = .$$QMAKE_EXTENSION_STATICLIB + !mingw|qtConfig(debug_and_release): debug_suffix = "d" QMAKE_LIBS_OPENGL_ES2 = $${path}$${ext} $$QMAKE_LIBS_OPENGL_ES2 - QMAKE_LIBS_OPENGL_ES2_DEBUG = $${path}d$${ext} $$QMAKE_LIBS_OPENGL_ES2_DEBUG + QMAKE_LIBS_OPENGL_ES2_DEBUG = $${path}$${debug_suffix}$${ext} $$QMAKE_LIBS_OPENGL_ES2_DEBUG export(QMAKE_LIBS_OPENGL_ES2) export(QMAKE_LIBS_OPENGL_ES2_DEBUG) } -- cgit v1.2.3 From 783d574b932288b61f915b28d5b7b9c5a979f58e Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 6 Mar 2020 13:38:17 -0800 Subject: CBOR support: prevent overflowing QByteArray's max allocation QByteArray doesn't like it. Apply the same protection to QString, which we know uses the same backend but uses elements twice as big. That means it can contain slightly more than half as many elements, but exact half will suffice for our needs. Change-Id: Iaa63461109844e978376fffd15f9d4c7a9137856 Reviewed-by: Edward Welbourne --- src/3rdparty/tinycbor/tests/parser/data.cpp | 37 +++--- src/corelib/serialization/qcborstream.cpp | 35 ++++-- src/corelib/serialization/qcborvalue.cpp | 27 +++-- src/corelib/text/qbytearray_p.h | 7 +- .../serialization/cborlargedatavalidation.cpp | 134 +++++++++++++++++++++ .../qcborstreamreader/qcborstreamreader.pro | 2 +- .../qcborstreamreader/tst_qcborstreamreader.cpp | 49 +++++++- .../serialization/qcborvalue/qcborvalue.pro | 2 +- .../serialization/qcborvalue/tst_qcborvalue.cpp | 52 ++++++-- 9 files changed, 294 insertions(+), 51 deletions(-) create mode 100644 tests/auto/corelib/serialization/cborlargedatavalidation.cpp diff --git a/src/3rdparty/tinycbor/tests/parser/data.cpp b/src/3rdparty/tinycbor/tests/parser/data.cpp index 0ab0e47be4..3523c32167 100644 --- a/src/3rdparty/tinycbor/tests/parser/data.cpp +++ b/src/3rdparty/tinycbor/tests/parser/data.cpp @@ -338,7 +338,7 @@ void addValidationColumns() QTest::addColumn("expectedError"); } -void addValidationData() +void addValidationData(size_t minInvalid = ~size_t(0)) { // illegal numbers are future extension points QTest::newRow("illegal-number-in-unsigned-1") << raw("\x81\x1c") << 0 << CborErrorIllegalNumber; @@ -488,26 +488,35 @@ void addValidationData() QTest::newRow("map-break-after-value-tag2") << raw("\x81\xbf\0\xd8\x20\xff") << 0 << CborErrorUnexpectedBreak; // check for pointer additions wrapping over the limit of the address space - CborError tooLargeOn32bit = (sizeof(void *) == 4) ? CborErrorDataTooLarge : CborErrorUnexpectedEOF; + auto wraparoundError = [minInvalid](uint64_t encodedSize) { + if (encodedSize > minInvalid) + return CborErrorDataTooLarge; + return CborErrorUnexpectedEOF; + }; + constexpr uint64_t FourGB = UINT32_MAX + UINT64_C(1); // on 32-bit systems, this is a -1 - QTest::newRow("bytearray-wraparound1") << raw("\x81\x5a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-wraparound1") << raw("\x81\x7a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-wraparound1") << raw("\x81\x5a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX); + QTest::newRow("string-wraparound1") << raw("\x81\x7a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX); // on 32-bit systems, a 4GB addition could be dropped - QTest::newRow("bytearray-wraparound2") << raw("\x81\x5b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit; - QTest::newRow("string-wraparound2") << raw("\x81\x7b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit; + QTest::newRow("bytearray-wraparound2") << raw("\x81\x5b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB); + QTest::newRow("string-wraparound2") << raw("\x81\x7b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB); // on 64-bit systems, this could be a -1 - QTest::newRow("bytearray-wraparound3") << raw("\x81\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; - QTest::newRow("string-wraparound3") << raw("\x81\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; + QTest::newRow("bytearray-wraparound3") << raw("\x81\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 + << wraparoundError(UINT64_MAX); + QTest::newRow("string-wraparound3") << raw("\x81\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 + << wraparoundError(UINT64_MAX); // ditto on chunks - QTest::newRow("bytearray-chunk-wraparound1") << raw("\x81\x5f\x5a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunk-wraparound1") << raw("\x81\x7f\x7a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunk-wraparound1") << raw("\x81\x5f\x5a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX); + QTest::newRow("string-chunk-wraparound1") << raw("\x81\x7f\x7a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX); // on 32-bit systems, a 4GB addition could be dropped - QTest::newRow("bytearray-chunk-wraparound2") << raw("\x81\x5f\x5b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit; - QTest::newRow("string-chunk-wraparound2") << raw("\x81\x7f\x7b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit; + QTest::newRow("bytearray-chunk-wraparound2") << raw("\x81\x5f\x5b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB); + QTest::newRow("string-chunk-wraparound2") << raw("\x81\x7f\x7b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB); // on 64-bit systems, this could be a -1 - QTest::newRow("bytearray-chunk-wraparound3") << raw("\x81\x5f\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; - QTest::newRow("string-chunk-wraparound3") << raw("\x81\x7f\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; + QTest::newRow("bytearray-chunk-wraparound3") << raw("\x81\x5f\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 + << wraparoundError(UINT64_MAX); + QTest::newRow("string-chunk-wraparound3") << raw("\x81\x7f\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 + << wraparoundError(UINT64_MAX); QTest::newRow("eof-after-array") << raw("\x81") << 0 << CborErrorUnexpectedEOF; QTest::newRow("eof-after-array2") << raw("\x81\x78\x20") << 0 << CborErrorUnexpectedEOF; diff --git a/src/corelib/serialization/qcborstream.cpp b/src/corelib/serialization/qcborstream.cpp index c598eee213..22748e49e1 100644 --- a/src/corelib/serialization/qcborstream.cpp +++ b/src/corelib/serialization/qcborstream.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 Intel Corporation. +** Copyright (C) 2020 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -39,6 +39,7 @@ #include "qcborstream.h" +#include #include #include #include @@ -2282,6 +2283,10 @@ bool QCborStreamReader::next(int maxRecursion) } else if (isString() || isByteArray()) { auto r = _readByteArray_helper(); while (r.status == Ok) { + if (isString() && r.data.size() > MaxStringSize) { + d->handleError(CborErrorDataTooLarge); + break; + } if (isString() && !QUtf8::isValidUtf8(r.data, r.data.size()).isValidUtf8) { d->handleError(CborErrorInvalidUtf8TextString); break; @@ -2564,15 +2569,23 @@ QCborStreamReader::StringResult QCborStreamReader::_readString_helper() result.status = r.status; if (r.status == Ok) { - QTextCodec::ConverterState cs; - result.data = QUtf8::convertToUnicode(r.data, r.data.size(), &cs); - if (cs.invalidChars == 0 && cs.remainingChars == 0) - return result; + // See QUtf8::convertToUnicode() a detailed explanation of why this + // conversion uses the same number of words or less. + CborError err = CborNoError; + if (r.data.size() > MaxStringSize) { + err = CborErrorDataTooLarge; + } else { + QTextCodec::ConverterState cs; + result.data = QUtf8::convertToUnicode(r.data, r.data.size(), &cs); + if (cs.invalidChars != 0 || cs.remainingChars != 0) + err = CborErrorInvalidUtf8TextString; + } - d->handleError(CborErrorInvalidUtf8TextString); - result.data.clear(); - result.status = Error; - return result; + if (err) { + d->handleError(err); + result.data.clear(); + result.status = Error; + } } return result; } @@ -2600,6 +2613,10 @@ QCborStreamReader::StringResult QCborStreamReader::_readByteArray_he qsizetype len = _currentStringChunkSize(); if (len < 0) return result; + if (len > MaxByteArraySize) { + d->handleError(CborErrorDataTooLarge); + return result; + } result.data.resize(len); auto r = readStringChunk(result.data.data(), len); diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index f4ceba3836..65abf8dba8 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 Intel Corporation. +** Copyright (C) 2020 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -46,6 +46,7 @@ #include #include +#include #include #include @@ -1534,6 +1535,8 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) // and calculate the final size if (add_overflow(offset, increment, &newSize)) return -1; + if (newSize > MaxByteArraySize) + return -1; // since usedData <= data.size(), this can't overflow usedData += increment; @@ -1608,13 +1611,6 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) setErrorInReader(reader, { QCborError::DataTooLarge }); } - if (r.status == QCborStreamReader::Error) { - // There can only be errors if there was data to be read. - Q_ASSERT(e.flags & Element::HasByteData); - data.truncate(e.value); - return; - } - // update size if (e.flags & Element::HasByteData) { auto b = new (dataPtr() + e.value) ByteData; @@ -1626,6 +1622,21 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) Q_ASSERT(e.type == QCborValue::String); e.flags |= Element::StringIsAscii; } + + // check that this UTF-8 text string can be loaded onto a QString + if (e.type == QCborValue::String) { + if (Q_UNLIKELY(b->len > MaxStringSize)) { + setErrorInReader(reader, { QCborError::DataTooLarge }); + r.status = QCborStreamReader::Error; + } + } + } + + if (r.status == QCborStreamReader::Error) { + // There can only be errors if there was data to be read. + Q_ASSERT(e.flags & Element::HasByteData); + data.truncate(e.value); + return; } elements.append(e); diff --git a/src/corelib/text/qbytearray_p.h b/src/corelib/text/qbytearray_p.h index 3c6257f786..ffec6dca22 100644 --- a/src/corelib/text/qbytearray_p.h +++ b/src/corelib/text/qbytearray_p.h @@ -56,10 +56,9 @@ QT_BEGIN_NAMESPACE -enum { - // Define as enum to force inlining. Don't expose MaxAllocSize in a public header. - MaxByteArraySize = MaxAllocSize - sizeof(std::remove_pointer::type) -}; +// -1 because of the terminating NUL +constexpr qsizetype MaxByteArraySize = MaxAllocSize - sizeof(std::remove_pointer::type) - 1; +constexpr qsizetype MaxStringSize = (MaxAllocSize - sizeof(std::remove_pointer::type)) / 2 - 1; QT_END_NAMESPACE diff --git a/tests/auto/corelib/serialization/cborlargedatavalidation.cpp b/tests/auto/corelib/serialization/cborlargedatavalidation.cpp new file mode 100644 index 0000000000..9abfe0f575 --- /dev/null +++ b/tests/auto/corelib/serialization/cborlargedatavalidation.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +namespace { +// A QIODevice that supplies a fixed header followed by a large sequence of +// null bytes up until a pre-determined size. +class LargeIODevice final : public QIODevice +{ +public: + qint64 realSize; + QByteArray start; + + LargeIODevice(const QByteArray &start, qint64 size, QObject *parent = nullptr) + : QIODevice(parent), realSize(size), start(start) + {} + + qint64 size() const override { return realSize; } + bool isSequential() const override { return false; } + +protected: + qint64 readData(char *data, qint64 maxlen) override; + qint64 writeData(const char *, qint64) override { return -1; } +}; +}; + +qint64 LargeIODevice::readData(char *data, qint64 maxlen) +{ + qint64 p = pos(); + if (maxlen > realSize - p) + maxlen = realSize - p; + memset(data, '\0', maxlen); + + qint64 fromstart = start.size() - p; + if (fromstart > maxlen) + fromstart = maxlen; + else if (fromstart < 0) + fromstart = 0; + if (fromstart) + memcpy(data, start.constData() + p, fromstart); + return maxlen; +} + +void addValidationLargeData(qsizetype minInvalid, qsizetype maxInvalid) +{ + char toolong[2 + sizeof(qsizetype)] = { char(0x81) }; + for (qsizetype v = maxInvalid; v >= minInvalid; --v) { + // 0x5a for 32-bit, 0x5b for 64-bit + toolong[1] = sizeof(v) > 4 ? 0x5b : 0x5a; + qToBigEndian(v, toolong + 2); + + QTest::addRow("bytearray-too-big-for-qbytearray-%llx", v) + << QByteArray(toolong, sizeof(toolong)) << 0 << CborErrorDataTooLarge; + toolong[1] |= 0x20; + + // QCborStreamReader::readString copies to a QByteArray first + QTest::addRow("string-too-big-for-qbytearray-%llx", v) + << QByteArray(toolong, sizeof(toolong)) << 0 << CborErrorDataTooLarge; + } +} + +void addValidationHugeDevice(qsizetype byteArrayInvalid, qsizetype stringInvalid) +{ + qRegisterMetaType>(); + QTest::addColumn>("device"); + QTest::addColumn("expectedError"); + + char buf[1 + sizeof(quint64)]; + auto device = [&buf](QCborStreamReader::Type t, quint64 size) { + buf[0] = quint8(t) | 0x1b; + qToBigEndian(size, buf + 1); + size += sizeof(buf); + QSharedPointer p = + QSharedPointer::create(QByteArray(buf, sizeof(buf)), size); + return p; + }; + + // do the exact limits + QTest::newRow("bytearray-just-too-big") + << device(QCborStreamReader::ByteArray, byteArrayInvalid) << CborErrorDataTooLarge; + QTest::newRow("string-just-too-big") + << device(QCborStreamReader::String, stringInvalid) << CborErrorDataTooLarge; + + auto addSize = [=](const char *sizename, qint64 size) { + if (byteArrayInvalid < size) + QTest::addRow("bytearray-%s", sizename) + << device(QCborStreamReader::ByteArray, size) << CborErrorDataTooLarge; + if (stringInvalid < size) + QTest::addRow("string-%s", sizename) + << device(QCborStreamReader::String, size) << CborErrorDataTooLarge; + }; + addSize("1GB", quint64(1) << 30); + addSize("2GB", quint64(1) << 31); + addSize("4GB", quint64(1) << 32); + addSize("max", std::numeric_limits::max() - sizeof(buf)); +} diff --git a/tests/auto/corelib/serialization/qcborstreamreader/qcborstreamreader.pro b/tests/auto/corelib/serialization/qcborstreamreader/qcborstreamreader.pro index 5df331314a..b758de1a9e 100644 --- a/tests/auto/corelib/serialization/qcborstreamreader/qcborstreamreader.pro +++ b/tests/auto/corelib/serialization/qcborstreamreader/qcborstreamreader.pro @@ -1,4 +1,4 @@ -QT = core testlib +QT = core-private testlib TARGET = tst_qcborstreamreader CONFIG += testcase SOURCES += \ diff --git a/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp b/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp index 28d29168fb..f969bb9074 100644 --- a/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp +++ b/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 Intel Corporation. +** Copyright (C) 2020 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -40,6 +40,8 @@ #include #include +#include + class tst_QCborStreamReader : public QObject { Q_OBJECT @@ -73,6 +75,8 @@ private Q_SLOTS: void next(); void validation_data(); void validation(); + void hugeDeviceValidation_data(); + void hugeDeviceValidation(); void recursionLimit_data(); void recursionLimit(); @@ -902,16 +906,26 @@ void tst_QCborStreamReader::next() QVERIFY(doit("\xbf\x9f\1\xff\x9f" + data + "\xff\xff")); } +#include "../cborlargedatavalidation.cpp" + void tst_QCborStreamReader::validation_data() { + // Add QCborStreamReader-specific limitations due to use of QByteArray and + // QString, which are allocated by QArrayData::allocate(). + const qsizetype MaxInvalid = std::numeric_limits::max(); + const qsizetype MinInvalid = MaxByteArraySize + 1; + addValidationColumns(); - addValidationData(); + addValidationData(MinInvalid); + addValidationLargeData(MinInvalid, MaxInvalid); } void tst_QCborStreamReader::validation() { QFETCH_GLOBAL(bool, useDevice); QFETCH(QByteArray, data); + QFETCH(CborError, expectedError); + QCborError error = { QCborError::Code(expectedError) }; QBuffer buffer(&data); QCborStreamReader reader(data); @@ -920,12 +934,39 @@ void tst_QCborStreamReader::validation() reader.setDevice(&buffer); } parse(reader, data); - QVERIFY(reader.lastError() != QCborError::NoError); + QCOMPARE(reader.lastError(), error); + + // next() should fail + reader.reset(); + QVERIFY(!reader.next()); + QCOMPARE(reader.lastError(), error); +} + +void tst_QCborStreamReader::hugeDeviceValidation_data() +{ + addValidationHugeDevice(MaxByteArraySize + 1, MaxStringSize + 1); +} + +void tst_QCborStreamReader::hugeDeviceValidation() +{ + QFETCH_GLOBAL(bool, useDevice); + if (!useDevice) + return; + + QFETCH(QSharedPointer, device); + QFETCH(CborError, expectedError); + QCborError error = { QCborError::Code(expectedError) }; + + device->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + QCborStreamReader reader(device.data()); + + QVERIFY(parseOne(reader).isEmpty()); + QCOMPARE(reader.lastError(), error); // next() should fail reader.reset(); QVERIFY(!reader.next()); - QVERIFY(reader.lastError() != QCborError::NoError); + QCOMPARE(reader.lastError(), error); } static const int Recursions = 3; diff --git a/tests/auto/corelib/serialization/qcborvalue/qcborvalue.pro b/tests/auto/corelib/serialization/qcborvalue/qcborvalue.pro index 9dd67da1f0..4d01b290f5 100644 --- a/tests/auto/corelib/serialization/qcborvalue/qcborvalue.pro +++ b/tests/auto/corelib/serialization/qcborvalue/qcborvalue.pro @@ -1,4 +1,4 @@ -QT = core testlib +QT = core-private testlib TARGET = tst_qcborvalue CONFIG += testcase SOURCES += \ diff --git a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp index 49bb9cc144..488771d059 100644 --- a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp +++ b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 Intel Corporation. +** Copyright (C) 2020 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -40,6 +40,8 @@ #include #include +#include + Q_DECLARE_METATYPE(QCborKnownTags) Q_DECLARE_METATYPE(QCborValue) Q_DECLARE_METATYPE(QCborValue::EncodingOptions) @@ -102,6 +104,8 @@ private slots: void fromCborStreamReaderIODevice(); void validation_data(); void validation(); + void hugeDeviceValidation_data(); + void hugeDeviceValidation(); void recursionLimit_data(); void recursionLimit(); void toDiagnosticNotation_data(); @@ -1689,10 +1693,17 @@ void tst_QCborValue::fromCborStreamReaderIODevice() fromCbor_common(doCheck); } +#include "../cborlargedatavalidation.cpp" + void tst_QCborValue::validation_data() { + // Add QCborStreamReader-specific limitations due to use of QByteArray and + // QString, which are allocated by QArrayData::allocate(). + const qsizetype MaxInvalid = std::numeric_limits::max(); + const qsizetype MinInvalid = MaxByteArraySize + 1; addValidationColumns(); - addValidationData(); + addValidationData(MinInvalid); + addValidationLargeData(MinInvalid, MaxInvalid); // These tests say we have arrays and maps with very large item counts. // They are meant to ensure we don't pre-allocate a lot of memory @@ -1700,28 +1711,49 @@ void tst_QCborValue::validation_data() // elements in the stream is only 2, so we should get an unexpected EOF // error. QCborValue internally uses 16 bytes per element, so we get to // 2 GB at 2^27 elements. - QTest::addRow("very-large-array-no-overflow") << raw("\x9a\x07\xff\xff\xff" "\0\0"); - QTest::addRow("very-large-array-overflow1") << raw("\x9a\x40\0\0\0" "\0\0"); + QTest::addRow("very-large-array-no-overflow") << raw("\x9a\x07\xff\xff\xff" "\0\0") << 0 << CborErrorUnexpectedEOF; + QTest::addRow("very-large-array-overflow1") << raw("\x9a\x40\0\0\0" "\0\0") << 0 << CborErrorUnexpectedEOF; // this makes sure we don't accidentally clip to 32-bit: sending 2^32+2 elements - QTest::addRow("very-large-array-overflow2") << raw("\x9b\0\0\0\1""\0\0\0\2" "\0\0"); + QTest::addRow("very-large-array-overflow2") << raw("\x9b\0\0\0\1""\0\0\0\2" "\0\0") << 0 << CborErrorDataTooLarge; } void tst_QCborValue::validation() { QFETCH(QByteArray, data); + QFETCH(CborError, expectedError); + QCborError error = { QCborError::Code(expectedError) }; - QCborParserError error; - QCborValue decoded = QCborValue::fromCbor(data, &error); - QVERIFY(error.error != QCborError{}); + QCborParserError parserError; + QCborValue decoded = QCborValue::fromCbor(data, &parserError); + QCOMPARE(parserError.error, error); if (data.startsWith('\x81')) { // decode without the array prefix - decoded = QCborValue::fromCbor(data.mid(1), &error); - QVERIFY(error.error != QCborError{}); + char *ptr = const_cast(data.constData()); + QByteArray mid = QByteArray::fromRawData(ptr + 1, data.size() - 1); + decoded = QCborValue::fromCbor(mid, &parserError); + QCOMPARE(parserError.error, error); } } +void tst_QCborValue::hugeDeviceValidation_data() +{ + addValidationHugeDevice(MaxByteArraySize + 1, MaxStringSize + 1); +} + +void tst_QCborValue::hugeDeviceValidation() +{ + QFETCH(QSharedPointer, device); + QFETCH(CborError, expectedError); + QCborError error = { QCborError::Code(expectedError) }; + + device->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + QCborStreamReader reader(device.data()); + QCborValue decoded = QCborValue::fromCbor(reader); + QCOMPARE(reader.lastError(), error); +} + void tst_QCborValue::recursionLimit_data() { constexpr int RecursionAttempts = 4096; -- cgit v1.2.3 From bff56f953adc8ce743aa774d6ad09725d8b9bc45 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 20 Mar 2020 17:55:31 -0300 Subject: tst_QCborValue: Prepare for 64-bit QVectors in Qt 6 Change-Id: Ief61acdfbe4d4b5ba1f0fffd15fe1e921aab0a72 Reviewed-by: Lars Knoll --- .../serialization/qcborvalue/tst_qcborvalue.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp index 488771d059..6d8161c1f9 100644 --- a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp +++ b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp @@ -1709,13 +1709,21 @@ void tst_QCborValue::validation_data() // They are meant to ensure we don't pre-allocate a lot of memory // unnecessarily and possibly crash the application. The actual number of // elements in the stream is only 2, so we should get an unexpected EOF - // error. QCborValue internally uses 16 bytes per element, so we get to - // 2 GB at 2^27 elements. - QTest::addRow("very-large-array-no-overflow") << raw("\x9a\x07\xff\xff\xff" "\0\0") << 0 << CborErrorUnexpectedEOF; - QTest::addRow("very-large-array-overflow1") << raw("\x9a\x40\0\0\0" "\0\0") << 0 << CborErrorUnexpectedEOF; - - // this makes sure we don't accidentally clip to 32-bit: sending 2^32+2 elements - QTest::addRow("very-large-array-overflow2") << raw("\x9b\0\0\0\1""\0\0\0\2" "\0\0") << 0 << CborErrorDataTooLarge; + // error. QCborValue internally uses 16 bytes per element, so we get to 2 + // GB at 2^27 elements (32-bit) or, theoretically, 2^63 bytes at 2^59 + // elements (64-bit). + if (sizeof(QVector::size_type) == sizeof(int)) { + // 32-bit sizes (Qt 5 and 32-bit platforms) + QTest::addRow("very-large-array-no-overflow") << raw("\x9a\x07\xff\xff\xff" "\0\0") << 0 << CborErrorUnexpectedEOF; + QTest::addRow("very-large-array-overflow1") << raw("\x9a\x40\0\0\0" "\0\0") << 0 << CborErrorUnexpectedEOF; + + // this makes sure we don't accidentally clip to 32-bit: sending 2^32+2 elements + QTest::addRow("very-large-array-overflow2") << raw("\x9b\0\0\0\1""\0\0\0\2" "\0\0") << 0 << CborErrorDataTooLarge; + } else { + // 64-bit Qt 6 + QTest::addRow("very-large-array-no-overflow") << raw("\x9b\x07\xff\xff\xff" "\xff\xff\xff\xff" "\0\0"); + QTest::addRow("very-large-array-overflow") << raw("\x9b\x40\0\0\0" "\0\0\0\0" "\0\0"); + } } void tst_QCborValue::validation() -- cgit v1.2.3 From 7eb7bb979e2921e711eef03a9ea31af305935cac Mon Sep 17 00:00:00 2001 From: Joni Poikelin Date: Fri, 27 Mar 2020 12:34:59 +0200 Subject: Fix Fusion style ignoring SH_UnderlineShortcut from proxy style Fixes: QTBUG-83133 Change-Id: If3fbfae515567d9a0136beb35ca1f9f59460894b Reviewed-by: Andy Shaw --- src/widgets/styles/qfusionstyle.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp index 3fbd127efe..f489ca9c03 100644 --- a/src/widgets/styles/qfusionstyle.cpp +++ b/src/widgets/styles/qfusionstyle.cpp @@ -1532,7 +1532,7 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio //draw text QPalette::ColorRole textRole = dis ? QPalette::Text : QPalette::HighlightedText; uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; - if (!styleHint(SH_UnderlineShortcut, mbi, widget)) + if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget)) alignment |= Qt::TextHideMnemonic; proxy()->drawItemText(painter, item.rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole); } else { @@ -1702,7 +1702,7 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio p->save(); int t = s.indexOf(QLatin1Char('\t')); int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; - if (!styleHint(SH_UnderlineShortcut, menuitem, widget)) + if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget)) text_flags |= Qt::TextHideMnemonic; text_flags |= Qt::AlignLeft; if (t >= 0) { -- cgit v1.2.3 From c3b6d09caef755171c8cd113e83b8f6f6f54cb7d Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Fri, 27 Mar 2020 15:39:04 +0100 Subject: Update bundled libjpeg-turbo to version 2.0.4 [ChangeLog][Third-Party Code] libjpeg-turbo was updated to version 2.0.4 Change-Id: I7f74af0dc774a2172ff59713613a706e80d5b2cb Reviewed-by: Liang Qi --- src/3rdparty/libjpeg/jconfig.h | 2 +- src/3rdparty/libjpeg/jconfigint.h | 2 +- src/3rdparty/libjpeg/qt_attribution.json | 2 +- src/3rdparty/libjpeg/src/ChangeLog.md | 87 ++++++++++++++++++++++++-------- src/3rdparty/libjpeg/src/README.md | 34 ++++++------- src/3rdparty/libjpeg/src/jchuff.c | 6 +-- src/3rdparty/libjpeg/src/jcphuff.c | 4 +- src/3rdparty/libjpeg/src/jfdctint.c | 2 +- src/3rdparty/libjpeg/src/jidctint.c | 2 +- src/3rdparty/libjpeg/src/jidctred.c | 2 +- 10 files changed, 93 insertions(+), 50 deletions(-) diff --git a/src/3rdparty/libjpeg/jconfig.h b/src/3rdparty/libjpeg/jconfig.h index fb1e88ae29..8ed5620ca3 100644 --- a/src/3rdparty/libjpeg/jconfig.h +++ b/src/3rdparty/libjpeg/jconfig.h @@ -2,7 +2,7 @@ #define JPEG_LIB_VERSION 80 -#define LIBJPEG_TURBO_VERSION 2.0.3 +#define LIBJPEG_TURBO_VERSION 2.0.4 #define LIBJPEG_TURBO_VERSION_NUMBER 2000002 diff --git a/src/3rdparty/libjpeg/jconfigint.h b/src/3rdparty/libjpeg/jconfigint.h index 6616918509..d945be285b 100644 --- a/src/3rdparty/libjpeg/jconfigint.h +++ b/src/3rdparty/libjpeg/jconfigint.h @@ -8,7 +8,7 @@ #define PACKAGE_NAME "libjpeg-turbo" -#define VERSION "2.0.3" +#define VERSION "2.0.4" #if SIZE_MAX == 0xffffffff #define SIZEOF_SIZE_T 4 diff --git a/src/3rdparty/libjpeg/qt_attribution.json b/src/3rdparty/libjpeg/qt_attribution.json index fa81529968..1eb095128a 100644 --- a/src/3rdparty/libjpeg/qt_attribution.json +++ b/src/3rdparty/libjpeg/qt_attribution.json @@ -6,7 +6,7 @@ "Description": "The Independent JPEG Group's JPEG software", "Homepage": "http://libjpeg-turbo.virtualgl.org/", - "Version": "2.0.3", + "Version": "2.0.4", "License": "Independent JPEG Group License", "LicenseId": "IJG", "LicenseFile": "LICENSE", diff --git a/src/3rdparty/libjpeg/src/ChangeLog.md b/src/3rdparty/libjpeg/src/ChangeLog.md index 3667d120b1..4d1219e555 100644 --- a/src/3rdparty/libjpeg/src/ChangeLog.md +++ b/src/3rdparty/libjpeg/src/ChangeLog.md @@ -1,3 +1,44 @@ +2.0.4 +===== + +### Significant changes relative to 2.0.3: + +1. Fixed a regression in the Windows packaging system (introduced by +2.0 beta1[2]) whereby, if both the 64-bit libjpeg-turbo SDK for GCC and the +64-bit libjpeg-turbo SDK for Visual C++ were installed on the same system, only +one of them could be uninstalled. + +2. Fixed a signed integer overflow and subsequent segfault that occurred when +attempting to decompress images with more than 715827882 pixels using the +64-bit C version of TJBench. + +3. Fixed out-of-bounds write in `tjDecompressToYUV2()` and +`tjDecompressToYUVPlanes()` (sometimes manifesting as a double free) that +occurred when attempting to decompress grayscale JPEG images that were +compressed with a sampling factor other than 1 (for instance, with +`cjpeg -grayscale -sample 2x2`). + +4. Fixed a regression introduced by 2.0.2[5] that caused the TurboJPEG API to +incorrectly identify some JPEG images with unusual sampling factors as 4:4:4 +JPEG images. This was known to cause a buffer overflow when attempting to +decompress some such images using `tjDecompressToYUV2()` or +`tjDecompressToYUVPlanes()`. + +5. Fixed an issue, detected by ASan, whereby attempting to losslessly transform +a specially-crafted malformed JPEG image containing an extremely-high-frequency +coefficient block (junk image data that could never be generated by a +legitimate JPEG compressor) could cause the Huffman encoder's local buffer to +be overrun. (Refer to 1.4.0[9] and 1.4beta1[15].) Given that the buffer +overrun was fully contained within the stack and did not cause a segfault or +other user-visible errant behavior, and given that the lossless transformer +(unlike the decompressor) is not generally exposed to arbitrary data exploits, +this issue did not likely pose a security risk. + +6. The ARM 64-bit (ARMv8) NEON SIMD assembly code now stores constants in a +separate read-only data section rather than in the text section, to support +execute-only memory layouts. + + 2.0.3 ===== @@ -138,10 +179,11 @@ would produce a "Bogus message code" error message if the underlying bitmap and PPM readers/writers threw an error that was specific to the readers/writers (as opposed to a general libjpeg API error.) -4. Fixed an issue whereby a specially-crafted malformed BMP file, one in which -the header specified an image width of 1073741824 pixels, would trigger a -floating point exception (division by zero) in the `tjLoadImage()` function -when attempting to load the BMP file into a 4-component image buffer. +4. Fixed an issue (CVE-2018-1152) whereby a specially-crafted malformed BMP +file, one in which the header specified an image width of 1073741824 pixels, +would trigger a floating point exception (division by zero) in the +`tjLoadImage()` function when attempting to load the BMP file into a +4-component image buffer. 5. Fixed an issue whereby certain combinations of calls to `jpeg_skip_scanlines()` and `jpeg_read_scanlines()` could trigger an infinite @@ -155,10 +197,10 @@ a 4:2:2 or 4:2:0 JPEG image using the merged (non-fancy) upsampling algorithms 7. The new CMake-based build system will now disable the MIPS DSPr2 SIMD extensions if it detects that the compiler does not support DSPr2 instructions. -8. Fixed out-of-bounds read in cjpeg that occurred when attempting to compress -a specially-crafted malformed color-index (8-bit-per-sample) BMP file in which -some of the samples (color indices) exceeded the bounds of the BMP file's color -table. +8. Fixed out-of-bounds read in cjpeg (CVE-2018-14498) that occurred when +attempting to compress a specially-crafted malformed color-index +(8-bit-per-sample) BMP file in which some of the samples (color indices) +exceeded the bounds of the BMP file's color table. 9. Fixed a signed integer overflow in the progressive Huffman decoder, detected by the Clang and GCC undefined behavior sanitizers, that could be triggered by @@ -318,8 +360,8 @@ write scanlines in bottom-up order.) djpeg will now exit gracefully if an output format other than PPM/PGM, GIF, or Targa is selected along with the `-crop` option. -4. Fixed an issue whereby `jpeg_skip_scanlines()` would segfault if color -quantization was enabled. +4. Fixed an issue (CVE-2017-15232) whereby `jpeg_skip_scanlines()` would +segfault if color quantization was enabled. 5. TJBench (both C and Java versions) will now display usage information if any command-line argument is unrecognized. This prevents the program from silently @@ -946,13 +988,13 @@ and IDCT algorithms (both are used during JPEG decompression.) For unknown reasons (probably related to clang), this code cannot currently be compiled for iOS. -15. Fixed an extremely rare bug that could cause the Huffman encoder's local -buffer to overrun when a very high-frequency MCU is compressed using quality -100 and no subsampling, and when the JPEG output buffer is being dynamically -resized by the destination manager. This issue was so rare that, even with a -test program specifically designed to make the bug occur (by injecting random -high-frequency YUV data into the compressor), it was reproducible only once in -about every 25 million iterations. +15. Fixed an extremely rare bug (CVE-2014-9092) that could cause the Huffman +encoder's local buffer to overrun when a very high-frequency MCU is compressed +using quality 100 and no subsampling, and when the JPEG output buffer is being +dynamically resized by the destination manager. This issue was so rare that, +even with a test program specifically designed to make the bug occur (by +injecting random high-frequency YUV data into the compressor), it was +reproducible only once in about every 25 million iterations. 16. Fixed an oversight in the TurboJPEG C wrapper: if any of the JPEG compression functions was called repeatedly with the same @@ -987,8 +1029,9 @@ entropy coding (by passing arguments of `-progressive -arithmetic` to cjpeg or jpegtran, for instance) would result in an error, `Requested feature was omitted at compile time`. -4. Fixed a couple of issues whereby malformed JPEG images would cause -libjpeg-turbo to use uninitialized memory during decompression. +4. Fixed a couple of issues (CVE-2013-6629 and CVE-2013-6630) whereby malformed +JPEG images would cause libjpeg-turbo to use uninitialized memory during +decompression. 5. Fixed an error (`Buffer passed to JPEG library is too small`) that occurred when calling the TurboJPEG YUV encoding function with a very small (< 5x5) @@ -1127,9 +1170,9 @@ correct behavior of the colorspace extensions when merged upsampling is used. upper 64 bits of xmm6 and xmm7 on Win64 platforms, which violated the Win64 calling conventions. -4. Fixed a regression caused by 1.2.0[6] whereby decompressing corrupt JPEG -images (specifically, images in which the component count was erroneously set -to a large value) would cause libjpeg-turbo to segfault. +4. Fixed a regression (CVE-2012-2806) caused by 1.2.0[6] whereby decompressing +corrupt JPEG images (specifically, images in which the component count was +erroneously set to a large value) would cause libjpeg-turbo to segfault. 5. Worked around a severe performance issue with "Bobcat" (AMD Embedded APU) processors. The `MASKMOVDQU` instruction, which was used by the libjpeg-turbo diff --git a/src/3rdparty/libjpeg/src/README.md b/src/3rdparty/libjpeg/src/README.md index c61b855644..e7ff743a47 100644 --- a/src/3rdparty/libjpeg/src/README.md +++ b/src/3rdparty/libjpeg/src/README.md @@ -1,14 +1,14 @@ Background ========== -libjpeg-turbo is a JPEG image codec that uses SIMD instructions (MMX, SSE2, -AVX2, NEON, AltiVec) to accelerate baseline JPEG compression and decompression -on x86, x86-64, ARM, and PowerPC systems, as well as progressive JPEG -compression on x86 and x86-64 systems. On such systems, libjpeg-turbo is -generally 2-6x as fast as libjpeg, all else being equal. On other types of -systems, libjpeg-turbo can still outperform libjpeg by a significant amount, by -virtue of its highly-optimized Huffman coding routines. In many cases, the -performance of libjpeg-turbo rivals that of proprietary high-speed JPEG codecs. +libjpeg-turbo is a JPEG image codec that uses SIMD instructions to accelerate +baseline JPEG compression and decompression on x86, x86-64, ARM, PowerPC, and +MIPS systems, as well as progressive JPEG compression on x86 and x86-64 +systems. On such systems, libjpeg-turbo is generally 2-6x as fast as libjpeg, +all else being equal. On other types of systems, libjpeg-turbo can still +outperform libjpeg by a significant amount, by virtue of its highly-optimized +Huffman coding routines. In many cases, the performance of libjpeg-turbo +rivals that of proprietary high-speed JPEG codecs. libjpeg-turbo implements both the traditional libjpeg API as well as the less powerful but more straightforward TurboJPEG API. libjpeg-turbo also features @@ -145,14 +145,14 @@ supported and which aren't. #### Fully supported -- **libjpeg: IDCT scaling extensions in decompressor**
+- **libjpeg API: IDCT scaling extensions in decompressor**
libjpeg-turbo supports IDCT scaling with scaling factors of 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, 9/8, 5/4, 11/8, 3/2, 13/8, 7/4, 15/8, and 2/1 (only 1/4 and 1/2 are SIMD-accelerated.) -- **libjpeg: Arithmetic coding** +- **libjpeg API: Arithmetic coding** -- **libjpeg: In-memory source and destination managers**
+- **libjpeg API: In-memory source and destination managers**
See notes below. - **cjpeg: Separate quality settings for luminance and chrominance**
@@ -184,14 +184,14 @@ means of quality improvement. The reader is invited to peruse the research at but it is the general belief of our project that these features have not demonstrated sufficient usefulness to justify inclusion in libjpeg-turbo. -- **libjpeg: DCT scaling in compressor**
+- **libjpeg API: DCT scaling in compressor**
`cinfo.scale_num` and `cinfo.scale_denom` are silently ignored. There is no technical reason why DCT scaling could not be supported when emulating the libjpeg v7+ API/ABI, but without the SmartScale extension (see below), only scaling factors of 1/2, 8/15, 4/7, 8/13, 2/3, 8/11, 4/5, and 8/9 would be available, which is of limited usefulness. -- **libjpeg: SmartScale**
+- **libjpeg API: SmartScale**
`cinfo.block_size` is silently ignored. SmartScale is an extension to the JPEG format that allows for DCT block sizes other than 8x8. Providing support for this new format would be @@ -204,7 +204,7 @@ demonstrated sufficient usefulness to justify inclusion in libjpeg-turbo. interest in providing this feature would be as a means of supporting additional DCT scaling factors. -- **libjpeg: Fancy downsampling in compressor**
+- **libjpeg API: Fancy downsampling in compressor**
`cinfo.do_fancy_downsampling` is silently ignored. This requires the DCT scaling feature, which is not supported. @@ -252,8 +252,8 @@ building libjpeg-turbo. This will restore the pre-1.3 behavior, in which libjpeg v8 API/ABI. On Un*x systems, including the in-memory source/destination managers changes -the dynamic library version from 62.1.0 to 62.2.0 if using libjpeg v6b API/ABI -emulation and from 7.1.0 to 7.2.0 if using libjpeg v7 API/ABI emulation. +the dynamic library version from 62.2.0 to 62.3.0 if using libjpeg v6b API/ABI +emulation and from 7.2.0 to 7.3.0 if using libjpeg v7 API/ABI emulation. Note that, on most Un*x systems, the dynamic linker will not look for a function in a library until that function is actually used. Thus, if a program @@ -329,7 +329,7 @@ in a way that makes the rest of the libjpeg infrastructure happy, so it is necessary to use the slow Huffman decoder when decompressing a JPEG image that has restart markers. This can cause the decompression performance to drop by as much as 20%, but the performance will still be much greater than that of -libjpeg. Many consumer packages, such as PhotoShop, use restart markers when +libjpeg. Many consumer packages, such as Photoshop, use restart markers when generating JPEG images, so images generated by those programs will experience this issue. diff --git a/src/3rdparty/libjpeg/src/jchuff.c b/src/3rdparty/libjpeg/src/jchuff.c index 526203e3db..cb05055d99 100644 --- a/src/3rdparty/libjpeg/src/jchuff.c +++ b/src/3rdparty/libjpeg/src/jchuff.c @@ -43,8 +43,8 @@ */ /* NOTE: Both GCC and Clang define __GNUC__ */ -#if defined __GNUC__ && (defined __arm__ || defined __aarch64__) -#if !defined __thumb__ || defined __thumb2__ +#if defined(__GNUC__) && (defined(__arm__) || defined(__aarch64__)) +#if !defined(__thumb__) || defined(__thumb2__) #define USE_CLZ_INTRINSIC #endif #endif @@ -432,7 +432,7 @@ dump_buffer(working_state *state) * scanning order-- 1, 8, 16, etc.), then this will produce an encoded block * larger than 200 bytes. */ -#define BUFSIZE (DCTSIZE2 * 4) +#define BUFSIZE (DCTSIZE2 * 8) #define LOAD_BUFFER() { \ if (state->free_in_buffer < BUFSIZE) { \ diff --git a/src/3rdparty/libjpeg/src/jcphuff.c b/src/3rdparty/libjpeg/src/jcphuff.c index 024d3af0fb..8c4efaf16c 100644 --- a/src/3rdparty/libjpeg/src/jcphuff.c +++ b/src/3rdparty/libjpeg/src/jcphuff.c @@ -52,8 +52,8 @@ */ /* NOTE: Both GCC and Clang define __GNUC__ */ -#if defined __GNUC__ && (defined __arm__ || defined __aarch64__) -#if !defined __thumb__ || defined __thumb2__ +#if defined(__GNUC__) && (defined(__arm__) || defined(__aarch64__)) +#if !defined(__thumb__) || defined(__thumb2__) #define USE_CLZ_INTRINSIC #endif #endif diff --git a/src/3rdparty/libjpeg/src/jfdctint.c b/src/3rdparty/libjpeg/src/jfdctint.c index c0391a92be..b47c3061ac 100644 --- a/src/3rdparty/libjpeg/src/jfdctint.c +++ b/src/3rdparty/libjpeg/src/jfdctint.c @@ -1,7 +1,7 @@ /* * jfdctint.c * - * This file was part of the Independent JPEG Group's software. + * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1996, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright (C) 2015, D. R. Commander. diff --git a/src/3rdparty/libjpeg/src/jidctint.c b/src/3rdparty/libjpeg/src/jidctint.c index 55573429f1..98425d5fd0 100644 --- a/src/3rdparty/libjpeg/src/jidctint.c +++ b/src/3rdparty/libjpeg/src/jidctint.c @@ -1,7 +1,7 @@ /* * jidctint.c * - * This file was part of the Independent JPEG Group's software. + * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1998, Thomas G. Lane. * Modification developed 2002-2009 by Guido Vollbeding. * libjpeg-turbo Modifications: diff --git a/src/3rdparty/libjpeg/src/jidctred.c b/src/3rdparty/libjpeg/src/jidctred.c index 1ff352f875..1dd65a94d9 100644 --- a/src/3rdparty/libjpeg/src/jidctred.c +++ b/src/3rdparty/libjpeg/src/jidctred.c @@ -1,7 +1,7 @@ /* * jidctred.c * - * This file was part of the Independent JPEG Group's software. + * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1998, Thomas G. Lane. * libjpeg-turbo Modifications: * Copyright (C) 2015, D. R. Commander. -- cgit v1.2.3 From 2e0c29a4bbe2b3ae427137e65f179b0550dcf169 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 11 Feb 2020 13:58:04 +0100 Subject: itemviews: Use the start of the current selection when getting the range When doing a shift-select while moving the mouse then the start point should be based on the start of the current selection and not the pressed position. If there is no current selection start index, then we can safely depend on pressed position as this will be the previous index pressed on. This resolves an issue introduced by e02293a76d21e7077f1952d4ed8af6c6d1970190 when fixing QTBUG-78797 Fixes: QTBUG-81542 Change-Id: Ia66c42b220452fdcbc8cfccc05dbc8a3911c3f5e Reviewed-by: Richard Moe Gustavsen --- src/widgets/itemviews/qabstractitemview.cpp | 12 +++-- .../qabstractitemview/tst_qabstractitemview.cpp | 58 ++++++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index f879af6ad2..73604cb8f4 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -1844,10 +1844,16 @@ void QAbstractItemView::mouseMoveEvent(QMouseEvent *event) || edit(index, NoEditTriggers, event)) return; - if (d->selectionMode != SingleSelection) - topLeft = d->pressedPosition - d->offset(); - else + if (d->selectionMode != SingleSelection) { + // Use the current selection start index if it is valid as this will be based on the + // start of the selection and not the last item being pressed which can be different + // when in extended selection + topLeft = d->currentSelectionStartIndex.isValid() + ? visualRect(d->currentSelectionStartIndex).center() + : d->pressedPosition - d->offset(); + } else { topLeft = bottomRight; + } d->checkMouseMove(index); diff --git a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp index bcfc477733..5828b099d6 100644 --- a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp +++ b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp @@ -151,6 +151,7 @@ private slots: void currentFollowsIndexWidget(); void checkFocusAfterActivationChanges_data(); void checkFocusAfterActivationChanges(); + void dragSelectAfterNewPress(); }; class MyAbstractItemDelegate : public QAbstractItemDelegate @@ -2514,5 +2515,62 @@ void tst_QAbstractItemView::checkFocusAfterActivationChanges() QVERIFY(view->hasFocus()); } +void tst_QAbstractItemView::dragSelectAfterNewPress() +{ + QStandardItemModel model; + for (int i = 0; i < 10; ++i) { + QStandardItem *item = new QStandardItem(QString::number(i)); + model.setItem(i, 0, item); + } + + QListView view; + view.setFixedSize(160, 650); // Minimum width for windows with frame on Windows 8 + view.setSelectionMode(QListView::ExtendedSelection); + view.setModel(&model); + centerOnScreen(&view); + moveCursorAway(&view); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QModelIndex index0 = model.index(0, 0); + QModelIndex index2 = model.index(2, 0); + + view.setCurrentIndex(index0); + QCOMPARE(view.currentIndex(), index0); + + // Select item 0 using a single click + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, + view.visualRect(index0).center()); + QCOMPARE(view.currentIndex(), index0); + + // Press to select item 2 + QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, + view.visualRect(index2).center()); + QCOMPARE(view.currentIndex(), index2); + + // Verify that the selection worked OK + QModelIndexList selected = view.selectionModel()->selectedIndexes(); + QCOMPARE(selected.count(), 3); + for (int i = 0; i < 2; ++i) + QVERIFY(selected.contains(model.index(i, 0))); + + QModelIndex index5 = model.index(5, 0); + const QPoint releasePos = view.visualRect(index5).center(); + // The mouse move event has to be created manually because the QTest framework does not + // contain a function for mouse moves with buttons pressed + QMouseEvent moveEvent2(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, + Qt::ShiftModifier); + const bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent2); + QVERIFY(moveEventReceived); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, releasePos); + QCOMPARE(view.currentIndex(), index5); + + // Verify that the selection worked OK + selected = view.selectionModel()->selectedIndexes(); + QCOMPARE(selected.count(), 6); + for (int i = 0; i < 5; ++i) + QVERIFY(selected.contains(model.index(i, 0))); +} + QTEST_MAIN(tst_QAbstractItemView) #include "tst_qabstractitemview.moc" -- cgit v1.2.3 From 68916fede41d1eca5d07eb6b1db518d41a007616 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 30 Mar 2020 12:07:29 +0200 Subject: Finish deprecating TouchPoint::rect, sceneRect and screenRect accessors All 6 getters and setters were deprecated by doc comment \obsolete in 3c159957f863cf8d367a9261e7016e52cd0348c1 (Qt 5.9). Now we will generate compiler warnings too. Change-Id: I94c6da607fa5758072af1287c9286b6c52179cfb Reviewed-by: Frederik Gladhorn --- src/gui/kernel/qevent.cpp | 30 +++++++----------------------- src/gui/kernel/qevent.h | 16 +++++++++++++--- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index bda0949764..466e70db30 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -4653,19 +4653,12 @@ QPointF QTouchEvent::TouchPoint::lastNormalizedPos() const return d->lastNormalizedPos; } +#if QT_DEPRECATED_SINCE(5, 15) /*! - Returns the rect for this touch point, relative to the widget - or QGraphicsItem that received the event. The rect is centered - around the point returned by pos(). - - \note This function returns an empty rect if the device does not report touch point sizes. - - \obsolete This function is deprecated in 5.9 because it returns the outer bounds + \deprecated This function is deprecated since 5.9 because it returns the outer bounds of the touchpoint regardless of rotation, whereas a touchpoint is more correctly modeled as an ellipse at position pos() with ellipseDiameters() which are independent of rotation(). - - \sa scenePos(), ellipseDiameters() */ QRectF QTouchEvent::TouchPoint::rect() const { @@ -4675,16 +4668,10 @@ QRectF QTouchEvent::TouchPoint::rect() const } /*! - Returns the rect for this touch point in scene coordinates. - - \note This function returns an empty rect if the device does not report touch point sizes. - - \obsolete This function is deprecated in 5.9 because it returns the outer bounds + \deprecated This function is deprecated since 5.9 because it returns the outer bounds of the touchpoint regardless of rotation, whereas a touchpoint is more correctly modeled as an ellipse at position scenePos() with ellipseDiameters() which are independent of rotation(). - - \sa scenePos(), ellipseDiameters() */ QRectF QTouchEvent::TouchPoint::sceneRect() const { @@ -4694,16 +4681,10 @@ QRectF QTouchEvent::TouchPoint::sceneRect() const } /*! - Returns the rect for this touch point in screen coordinates. - - \note This function returns an empty rect if the device does not report touch point sizes. - - \obsolete This function is deprecated because it returns the outer bounds of the + \deprecated This function is deprecated since 5.9 because it returns the outer bounds of the touchpoint regardless of rotation, whereas a touchpoint is more correctly modeled as an ellipse at position screenPos() with ellipseDiameters() which are independent of rotation(). - - \sa screenPos(), ellipseDiameters() */ QRectF QTouchEvent::TouchPoint::screenRect() const { @@ -4711,6 +4692,7 @@ QRectF QTouchEvent::TouchPoint::screenRect() const ret.moveCenter(d->screenPos); return ret; } +#endif /*! Returns the pressure of this touch point. The return value is in @@ -4909,6 +4891,7 @@ void QTouchEvent::TouchPoint::setLastNormalizedPos(const QPointF &lastNormalized d->lastNormalizedPos = lastNormalizedPos; } +#if QT_DEPRECATED_SINCE(5, 15) // ### remove the following 3 setRect functions and their usages soon /*! \internal \obsolete @@ -4942,6 +4925,7 @@ void QTouchEvent::TouchPoint::setScreenRect(const QRectF &screenRect) d->screenPos = screenRect.center(); d->ellipseDiameters = screenRect.size(); } +#endif /*! \internal */ void QTouchEvent::TouchPoint::setPressure(qreal pressure) diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index cf596d8d45..4aba9ff729 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -933,10 +933,23 @@ public: QPointF startNormalizedPos() const; QPointF lastNormalizedPos() const; +#if QT_DEPRECATED_SINCE(5, 15) + // All these are actually deprecated since 5.9, in docs + QT_DEPRECATED_VERSION_X_5_15("Use pos() and ellipseDiameters()") QRectF rect() const; + QT_DEPRECATED_VERSION_X_5_15("Use scenePos() and ellipseDiameters()") QRectF sceneRect() const; + QT_DEPRECATED_VERSION_X_5_15("Use screenPos() and ellipseDiameters()") QRectF screenRect() const; + // internal + QT_DEPRECATED_VERSION_X_5_15("Use setPos() and setEllipseDiameters()") + void setRect(const QRectF &rect); // deprecated + QT_DEPRECATED_VERSION_X_5_15("Use setScenePos() and setEllipseDiameters()") + void setSceneRect(const QRectF &sceneRect); // deprecated + QT_DEPRECATED_VERSION_X_5_15("Use setScreenPos() and setEllipseDiameters()") + void setScreenRect(const QRectF &screenRect); // deprecated +#endif qreal pressure() const; qreal rotation() const; QSizeF ellipseDiameters() const; @@ -961,9 +974,6 @@ public: void setLastScenePos(const QPointF &lastScenePos); void setLastScreenPos(const QPointF &lastScreenPos); void setLastNormalizedPos(const QPointF &lastNormalizedPos); - void setRect(const QRectF &rect); // deprecated - void setSceneRect(const QRectF &sceneRect); // deprecated - void setScreenRect(const QRectF &screenRect); // deprecated void setPressure(qreal pressure); void setRotation(qreal angle); void setEllipseDiameters(const QSizeF &dia); -- cgit v1.2.3 From 22fe29303869ccf31af25cd4eeeaad768c3f647b Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Fri, 27 Mar 2020 14:17:39 +0100 Subject: testlib: add QAbstractItemModelTester::verify() This amends b3e4be2d8b9debf217657436139da0152f6f8797. When building testlib with QtGui linked:(use "QT = core-private gui" in src/testlib/testlib.pro) Undefined symbols for architecture x86_64: "QAbstractItemModelTester::verify(bool, char const*, char const*, char const*, int)", referenced from: QTestPrivate::testDataGuiRoles(QAbstractItemModelTester*) in qabstractitemmodeltester.o Change-Id: Ideb10ddd6717fed8d9f91f75bbfc9d5a22104730 Reviewed-by: Giuseppe D'Angelo --- src/testlib/qabstractitemmodeltester.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/testlib/qabstractitemmodeltester.cpp b/src/testlib/qabstractitemmodeltester.cpp index 859966c0e3..f651fd95be 100644 --- a/src/testlib/qabstractitemmodeltester.cpp +++ b/src/testlib/qabstractitemmodeltester.cpp @@ -288,6 +288,12 @@ QAbstractItemModelTester::FailureReportingMode QAbstractItemModelTester::failure return d->failureReportingMode; } +bool QAbstractItemModelTester::verify(bool statement, const char *statementStr, const char *description, const char *file, int line) +{ + Q_D(QAbstractItemModelTester); + return d->verify(statement, statementStr, description, file, line); +} + QAbstractItemModelTesterPrivate::QAbstractItemModelTesterPrivate(QAbstractItemModel *model, QAbstractItemModelTester::FailureReportingMode failureReportingMode) : model(model), failureReportingMode(failureReportingMode), -- cgit v1.2.3 From cd57dae62ff80ad371ebcb3fe63731ebbf7ef808 Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Fri, 20 Mar 2020 11:54:14 +0100 Subject: Fuzzing: Add QGuiApplication to gui fuzz targets Change-Id: I3713701f63d9d8938fbb42ad1ae2f0c4ae813e94 Reviewed-by: Shawn Rutledge --- .../gui/painting/qcolorspace/fromiccprofile/fromiccprofile.pro | 1 + .../libfuzzer/gui/painting/qcolorspace/fromiccprofile/main.cpp | 8 ++++++-- tests/libfuzzer/gui/text/qtextdocument/sethtml/main.cpp | 10 +++++++--- tests/libfuzzer/gui/text/qtextdocument/sethtml/sethtml.pro | 3 ++- tests/libfuzzer/gui/text/qtextdocument/setmarkdown/main.cpp | 7 +++++++ .../gui/text/qtextdocument/setmarkdown/setmarkdown.pro | 4 ++-- .../libfuzzer/gui/text/qtextlayout/beginlayout/beginlayout.pro | 3 ++- tests/libfuzzer/gui/text/qtextlayout/beginlayout/main.cpp | 7 +++++++ 8 files changed, 34 insertions(+), 9 deletions(-) diff --git a/tests/libfuzzer/gui/painting/qcolorspace/fromiccprofile/fromiccprofile.pro b/tests/libfuzzer/gui/painting/qcolorspace/fromiccprofile/fromiccprofile.pro index 934ff81077..bed2198e0d 100644 --- a/tests/libfuzzer/gui/painting/qcolorspace/fromiccprofile/fromiccprofile.pro +++ b/tests/libfuzzer/gui/painting/qcolorspace/fromiccprofile/fromiccprofile.pro @@ -1,4 +1,5 @@ QT += gui +QTPLUGIN *= qminimal SOURCES += main.cpp FUZZ_ENGINE = $$(LIB_FUZZING_ENGINE) isEmpty(FUZZ_ENGINE) { diff --git a/tests/libfuzzer/gui/painting/qcolorspace/fromiccprofile/main.cpp b/tests/libfuzzer/gui/painting/qcolorspace/fromiccprofile/main.cpp index 7681c1468e..f663727d1a 100644 --- a/tests/libfuzzer/gui/painting/qcolorspace/fromiccprofile/main.cpp +++ b/tests/libfuzzer/gui/painting/qcolorspace/fromiccprofile/main.cpp @@ -34,8 +34,12 @@ // to reduce noise and increase speed. extern "C" int LLVMFuzzerTestOneInput(const char *data, size_t size) { - static int c = 0; - static QGuiApplication a(c, nullptr); + static int argc = 3; + static char arg1[] = "fuzzer"; + static char arg2[] = "-platform"; + static char arg3[] = "minimal"; + static char *argv[] = {arg1, arg2, arg3, nullptr}; + static QGuiApplication qga(argc, argv); QColorSpace cs = QColorSpace::fromIccProfile(QByteArray::fromRawData(data, size)); return 0; } diff --git a/tests/libfuzzer/gui/text/qtextdocument/sethtml/main.cpp b/tests/libfuzzer/gui/text/qtextdocument/sethtml/main.cpp index 51fa3c9e0f..ed2a5c4e37 100644 --- a/tests/libfuzzer/gui/text/qtextdocument/sethtml/main.cpp +++ b/tests/libfuzzer/gui/text/qtextdocument/sethtml/main.cpp @@ -26,12 +26,16 @@ ** ****************************************************************************/ -#include +#include #include extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) { - static int c = 0; - static QApplication a(c, nullptr); + static int argc = 3; + static char arg1[] = "fuzzer"; + static char arg2[] = "-platform"; + static char arg3[] = "minimal"; + static char *argv[] = {arg1, arg2, arg3, nullptr}; + static QGuiApplication qga(argc, argv); QTextDocument().setHtml(QByteArray::fromRawData(Data, Size)); return 0; } diff --git a/tests/libfuzzer/gui/text/qtextdocument/sethtml/sethtml.pro b/tests/libfuzzer/gui/text/qtextdocument/sethtml/sethtml.pro index af5ef9e940..bed2198e0d 100644 --- a/tests/libfuzzer/gui/text/qtextdocument/sethtml/sethtml.pro +++ b/tests/libfuzzer/gui/text/qtextdocument/sethtml/sethtml.pro @@ -1,4 +1,5 @@ -QT += widgets +QT += gui +QTPLUGIN *= qminimal SOURCES += main.cpp FUZZ_ENGINE = $$(LIB_FUZZING_ENGINE) isEmpty(FUZZ_ENGINE) { diff --git a/tests/libfuzzer/gui/text/qtextdocument/setmarkdown/main.cpp b/tests/libfuzzer/gui/text/qtextdocument/setmarkdown/main.cpp index 66ddf738f2..6093da9827 100644 --- a/tests/libfuzzer/gui/text/qtextdocument/setmarkdown/main.cpp +++ b/tests/libfuzzer/gui/text/qtextdocument/setmarkdown/main.cpp @@ -26,9 +26,16 @@ ** ****************************************************************************/ +#include #include extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) { + static int argc = 3; + static char arg1[] = "fuzzer"; + static char arg2[] = "-platform"; + static char arg3[] = "minimal"; + static char *argv[] = {arg1, arg2, arg3, nullptr}; + static QGuiApplication qga(argc, argv); QTextDocument().setMarkdown(QByteArray::fromRawData(Data, Size)); return 0; } diff --git a/tests/libfuzzer/gui/text/qtextdocument/setmarkdown/setmarkdown.pro b/tests/libfuzzer/gui/text/qtextdocument/setmarkdown/setmarkdown.pro index 758622e1af..bed2198e0d 100644 --- a/tests/libfuzzer/gui/text/qtextdocument/setmarkdown/setmarkdown.pro +++ b/tests/libfuzzer/gui/text/qtextdocument/setmarkdown/setmarkdown.pro @@ -1,5 +1,5 @@ -CONFIG += console -CONFIG -= app_bundle +QT += gui +QTPLUGIN *= qminimal SOURCES += main.cpp FUZZ_ENGINE = $$(LIB_FUZZING_ENGINE) isEmpty(FUZZ_ENGINE) { diff --git a/tests/libfuzzer/gui/text/qtextlayout/beginlayout/beginlayout.pro b/tests/libfuzzer/gui/text/qtextlayout/beginlayout/beginlayout.pro index af5ef9e940..bed2198e0d 100644 --- a/tests/libfuzzer/gui/text/qtextlayout/beginlayout/beginlayout.pro +++ b/tests/libfuzzer/gui/text/qtextlayout/beginlayout/beginlayout.pro @@ -1,4 +1,5 @@ -QT += widgets +QT += gui +QTPLUGIN *= qminimal SOURCES += main.cpp FUZZ_ENGINE = $$(LIB_FUZZING_ENGINE) isEmpty(FUZZ_ENGINE) { diff --git a/tests/libfuzzer/gui/text/qtextlayout/beginlayout/main.cpp b/tests/libfuzzer/gui/text/qtextlayout/beginlayout/main.cpp index dfb9559241..27e0566c2c 100644 --- a/tests/libfuzzer/gui/text/qtextlayout/beginlayout/main.cpp +++ b/tests/libfuzzer/gui/text/qtextlayout/beginlayout/main.cpp @@ -26,9 +26,16 @@ ** ****************************************************************************/ +#include #include extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) { + static int argc = 3; + static char arg1[] = "fuzzer"; + static char arg2[] = "-platform"; + static char arg3[] = "minimal"; + static char *argv[] = {arg1, arg2, arg3, nullptr}; + static QGuiApplication qga(argc, argv); QTextLayout tl(QByteArray::fromRawData(Data, Size)); tl.beginLayout(); tl.endLayout(); -- cgit v1.2.3 From f5a58cccc2dad567ddda2fe621f21352781852f0 Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Mon, 23 Mar 2020 22:05:02 +0100 Subject: Fuzzing: ignore logging output from QColorSpace Change-Id: Ica549be24c8873854934f4ba24f2b3f7cb077e25 Reviewed-by: Allan Sandfeld Jensen --- .../libfuzzer/gui/painting/qcolorspace/fromiccprofile/main.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/libfuzzer/gui/painting/qcolorspace/fromiccprofile/main.cpp b/tests/libfuzzer/gui/painting/qcolorspace/fromiccprofile/main.cpp index f663727d1a..8a588c6e21 100644 --- a/tests/libfuzzer/gui/painting/qcolorspace/fromiccprofile/main.cpp +++ b/tests/libfuzzer/gui/painting/qcolorspace/fromiccprofile/main.cpp @@ -26,14 +26,16 @@ ** ****************************************************************************/ +#include + #include #include -// Run this with -// QT_LOGGING_RULES="qt.gui.icc=false" -// to reduce noise and increase speed. - extern "C" int LLVMFuzzerTestOneInput(const char *data, size_t size) { + // to reduce noise and increase speed + static char quiet[] = "QT_LOGGING_RULES=qt.gui.icc=false"; + static int pe = putenv(quiet); + Q_UNUSED(pe) static int argc = 3; static char arg1[] = "fuzzer"; static char arg2[] = "-platform"; -- cgit v1.2.3 From 8f88a3962a7b1716d2c0482b818d2504776ece05 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 30 Mar 2020 17:20:35 +0200 Subject: Fix 1 pixel wide images Images are rounded up to 4 bytes per line minimum, so one pixel wide images might not shrink when resizing. Fixes: QTBUG-83179 Change-Id: If72c94409e4c899c5ad05b2867f5f53a94d0580f Reviewed-by: Christian Kamm --- src/gui/image/qimage_conversions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 6ddd08d08d..853bbe4f8e 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -426,8 +426,8 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im data->nbytes = params.totalSize; } data->bytes_per_line = params.bytesPerLine; - data->depth = destDepth; } + data->depth = destDepth; data->format = dst_format; return true; } -- cgit v1.2.3 From d934fd7f54eae24ea3f719890e2c4dbbc445049d Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Fri, 6 Mar 2020 17:17:40 +0100 Subject: Send MouseMove events without buttons if the press closed the popup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With nested popup widgets, pressing a mouse button on the lower popup will close the active popup. MouseMove events that are generated before the button is released again should not have that button included, as it is likely to result in incorrect state handling in the widget. This change removes all buttons from the MouseMove event, which is the second best option. This is mostly consistent with the behavior when closing a popup and no other popup remains. The widget underneath will get MouseMove events without the respective button included. This change doesn't include a fix for the final release event, which should ideally also not be delivered to the remaining popup, as it never got a corresponding press event. Qt has already reset the states in which it stores which widget received the press event at the time the release is generated, such as qt_button_down and qt_popup_down. So we can't separate a release grabbed by a newly opened popup (which we want) from a release to the popup that became active after closing (which we don't want). However, widgets can more easily work around this issue, and the risk of breaking things by changing the code further becomes too high. Change-Id: I603bbdbc7e7355952d96ab77c5e2d2f1e6f94987 Fixes: QTBUG-82538 Reviewed-by: Tor Arne Vestbø Reviewed-by: Gatis Paeglis --- src/widgets/kernel/qwidgetwindow.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 596343c52f..7b36699d5c 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -505,7 +505,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ? QEvent::MouseButtonRelease : QEvent::MouseButtonPress; if (QApplicationPrivate::inPopupMode()) { - QWidget *activePopupWidget = QApplication::activePopupWidget(); + QPointer activePopupWidget = QApplication::activePopupWidget(); QPoint mapped = event->pos(); if (activePopupWidget != m_widget) mapped = activePopupWidget->mapFromGlobal(event->globalPos()); @@ -565,9 +565,11 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) #endif if ((event->type() != QEvent::MouseButtonPress) || !(event->flags().testFlag(Qt::MouseEventCreatedDoubleClick))) { - + // if the widget that was pressed is gone, then deliver move events without buttons + const auto buttons = event->type() == QEvent::MouseMove && qt_button_down == nullptr + ? Qt::NoButton : event->buttons(); QMouseEvent e(event->type(), widgetPos, event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers(), event->source()); + event->button(), buttons, event->modifiers(), event->source()); e.setTimestamp(event->timestamp()); QApplicationPrivate::sendMouseEvent(receiver, &e, receiver, receiver->window(), &qt_button_down, qt_last_mouse_receiver); qt_last_mouse_receiver = receiver; -- cgit v1.2.3 From 82a39f12fa50424fe792b4ff7e7764d98ebabe3e Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 30 Mar 2020 14:27:26 +0200 Subject: QCommonStyle::pixelMetric(): Silence warnings about deprecated enum values Add the Qt 6 code paths and enclose in warnings exclusions. Change-Id: I321296ef220fb788f04979ffff42a8a5f226dfdb Reviewed-by: Richard Moe Gustavsen --- src/widgets/styles/qcommonstyle.cpp | 45 +++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 45ac6712b4..b94c022a1f 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -4498,6 +4498,21 @@ QRect QCommonStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex return ret; } +static inline int defaultLayoutTopMargin(const QStyleOption *opt) +{ + return int(QStyleHelper::dpiScaled(11, opt)); +} + +static inline int defaultLayoutChildMargin(const QStyleOption *opt) +{ + return int(QStyleHelper::dpiScaled(9, opt)); +} + +static inline int defaultLayoutSpacing(const QStyleOption *opt) +{ + return int(QStyleHelper::dpiScaled(6, opt)); +} + /*! \reimp */ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWidget *widget) const { @@ -4776,28 +4791,44 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWid case PM_LayoutBottomMargin: { bool isWindow = false; - if (opt) { - isWindow = (opt->state & State_Window); - } else if (widget) { + if (opt) + isWindow = opt->state.testFlag(State_Window); + else if (widget) isWindow = widget->isWindow(); - } +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) + ret = isWindow ? defaultLayoutTopMargin(opt) : defaultLayoutChildMargin(opt); +#else +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED ret = proxy()->pixelMetric(isWindow ? PM_DefaultTopLevelMargin : PM_DefaultChildMargin, opt); +QT_WARNING_POP +#endif } break; case PM_LayoutHorizontalSpacing: case PM_LayoutVerticalSpacing: +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) + ret = defaultLayoutTopMargin(opt); +#else +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED ret = proxy()->pixelMetric(PM_DefaultLayoutSpacing, opt); +QT_WARNING_POP +#endif break; +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED case PM_DefaultTopLevelMargin: - ret = int(QStyleHelper::dpiScaled(11, opt)); + ret = defaultLayoutTopMargin(opt); break; case PM_DefaultChildMargin: - ret = int(QStyleHelper::dpiScaled(9, opt)); + ret = defaultLayoutChildMargin(opt); break; case PM_DefaultLayoutSpacing: - ret = int(QStyleHelper::dpiScaled(6, opt)); + ret = defaultLayoutSpacing(opt); break; +QT_WARNING_POP case PM_ToolBarIconSize: ret = 0; -- cgit v1.2.3 From ae653fc08b3dd670f8f9a7c49f861107ed61266f Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 31 Mar 2020 11:52:19 +0200 Subject: tst_QApplication::testDeleteLaterProcessEvents(): Split the test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test can trigger timeouts in COIN, split into subtests. Change-Id: I1fa5d52422275f89b2858d90c5979632aa7058e2 Reviewed-by: Mårten Nordheim --- .../kernel/qapplication/tst_qapplication.cpp | 149 +++++++++++---------- 1 file changed, 80 insertions(+), 69 deletions(-) diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp index 367a5767c4..a7a000a01e 100644 --- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp +++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp @@ -93,7 +93,11 @@ private slots: void quitOnLastWindowClosed(); void closeAllWindows(); void testDeleteLater(); - void testDeleteLaterProcessEvents(); + void testDeleteLaterProcessEvents1(); + void testDeleteLaterProcessEvents2(); + void testDeleteLaterProcessEvents3(); + void testDeleteLaterProcessEvents4(); + void testDeleteLaterProcessEvents5(); #if QT_CONFIG(library) void libraryPaths(); @@ -1333,10 +1337,8 @@ public slots: } }; -void tst_QApplication::testDeleteLaterProcessEvents() +void tst_QApplication::testDeleteLaterProcessEvents1() { - int argc = 0; - // Calling processEvents() with no event dispatcher does nothing. QObject *object = new QObject; QPointer p(object); @@ -1344,75 +1346,84 @@ void tst_QApplication::testDeleteLaterProcessEvents() QApplication::processEvents(); QVERIFY(p); delete object; +} - { - QApplication app(argc, nullptr); - // If you call processEvents() with an event dispatcher present, but - // outside any event loops, deferred deletes are not processed unless - // sendPostedEvents(0, DeferredDelete) is called. - object = new QObject; - p = object; - object->deleteLater(); - QCoreApplication::processEvents(); - QVERIFY(p); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QVERIFY(!p); - - // If you call deleteLater() on an object when there is no parent - // event loop, and then enter an event loop, the object will get - // deleted. - object = new QObject; - p = object; - object->deleteLater(); - QEventLoop loop; - QTimer::singleShot(1000, &loop, &QEventLoop::quit); - loop.exec(); - QVERIFY(!p); - } - { - // When an object is in an event loop, then calls deleteLater() and enters - // an event loop recursively, it should not die until the parent event - // loop continues. - QApplication app(argc, nullptr); - QEventLoop loop; - EventLoopNester *nester = new EventLoopNester; - p = nester; - QTimer::singleShot(3000, &loop, &QEventLoop::quit); - QTimer::singleShot(0, nester, &EventLoopNester::deleteLaterAndEnterLoop); - - loop.exec(); - QVERIFY(!p); - } - - { - // When the event loop that calls deleteLater() is exited - // immediately, the object should die when returning to the - // parent event loop - QApplication app(argc, nullptr); - QEventLoop loop; - EventLoopNester *nester = new EventLoopNester; - p = nester; - QTimer::singleShot(3000, &loop, &QEventLoop::quit); - QTimer::singleShot(0, nester, &EventLoopNester::deleteLaterAndExitLoop); +void tst_QApplication::testDeleteLaterProcessEvents2() +{ + int argc = 0; + QApplication app(argc, nullptr); + // If you call processEvents() with an event dispatcher present, but + // outside any event loops, deferred deletes are not processed unless + // sendPostedEvents(0, DeferredDelete) is called. + auto object = new QObject; + QPointer p(object); + object->deleteLater(); + QCoreApplication::processEvents(); + QVERIFY(p); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + QVERIFY(!p); + + // If you call deleteLater() on an object when there is no parent + // event loop, and then enter an event loop, the object will get + // deleted. + object = new QObject; + p = object; + object->deleteLater(); + QEventLoop loop; + QTimer::singleShot(1000, &loop, &QEventLoop::quit); + loop.exec(); + QVERIFY(!p); +} - loop.exec(); - QVERIFY(!p); - } +void tst_QApplication::testDeleteLaterProcessEvents3() +{ + int argc = 0; + // When an object is in an event loop, then calls deleteLater() and enters + // an event loop recursively, it should not die until the parent event + // loop continues. + QApplication app(argc, nullptr); + QEventLoop loop; + EventLoopNester *nester = new EventLoopNester; + QPointer p(nester); + QTimer::singleShot(3000, &loop, &QEventLoop::quit); + QTimer::singleShot(0, nester, &EventLoopNester::deleteLaterAndEnterLoop); + + loop.exec(); + QVERIFY(!p); +} - { - // when the event loop that calls deleteLater() also calls - // processEvents() immediately afterwards, the object should - // not die until the parent loop continues - QApplication app(argc, nullptr); - QEventLoop loop; - EventLoopNester *nester = new EventLoopNester(); - p = nester; - QTimer::singleShot(3000, &loop, &QEventLoop::quit); - QTimer::singleShot(0, nester, &EventLoopNester::deleteLaterAndProcessEvents); +void tst_QApplication::testDeleteLaterProcessEvents4() +{ + int argc = 0; + // When the event loop that calls deleteLater() is exited + // immediately, the object should die when returning to the + // parent event loop + QApplication app(argc, nullptr); + QEventLoop loop; + EventLoopNester *nester = new EventLoopNester; + QPointer p(nester); + QTimer::singleShot(3000, &loop, &QEventLoop::quit); + QTimer::singleShot(0, nester, &EventLoopNester::deleteLaterAndExitLoop); + + loop.exec(); + QVERIFY(!p); +} - loop.exec(); - QVERIFY(!p); - } +void tst_QApplication::testDeleteLaterProcessEvents5() +{ + // when the event loop that calls deleteLater() also calls + // processEvents() immediately afterwards, the object should + // not die until the parent loop continues + int argc = 0; + QApplication app(argc, nullptr); + QEventLoop loop; + EventLoopNester *nester = new EventLoopNester(); + QPointer p(nester); + QTimer::singleShot(3000, &loop, &QEventLoop::quit); + QTimer::singleShot(0, nester, &EventLoopNester::deleteLaterAndProcessEvents); + + loop.exec(); + QVERIFY(!p); } /* -- cgit v1.2.3 From 00d9f68c411a336e451065ae71b8d0fb0d44da60 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 31 Mar 2020 11:56:53 +0200 Subject: Speed up tst_QApplication::testDeleteLaterProcessEvents2() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quit the event loop once the object is destroyed. Change-Id: I6df1cfe867daacb6af56eb84646be91d98a2f545 Reviewed-by: Alex Trotsenko Reviewed-by: Mårten Nordheim --- tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp index a7a000a01e..3debfd4231 100644 --- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp +++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp @@ -1366,10 +1366,11 @@ void tst_QApplication::testDeleteLaterProcessEvents2() // If you call deleteLater() on an object when there is no parent // event loop, and then enter an event loop, the object will get // deleted. + QEventLoop loop; object = new QObject; + connect(object, &QObject::destroyed, &loop, &QEventLoop::quit); p = object; object->deleteLater(); - QEventLoop loop; QTimer::singleShot(1000, &loop, &QEventLoop::quit); loop.exec(); QVERIFY(!p); -- cgit v1.2.3 From 5c1bc5c8a41e9d4b40161eae1413bcb69325dd8f Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Fri, 27 Mar 2020 14:10:43 +0100 Subject: QLineEdit: clarify the impact of using validators Values that are validated as Intermediate are possible to enter, but returnPressed and editingFinished signals are not emitted. Fixes: QTBUG-82915 Change-Id: I3e194cd6ee93b3402090117b67044cf3663a232e Reviewed-by: Paul Wicking Reviewed-by: Edward Welbourne --- src/gui/util/qvalidator.cpp | 9 +++++++-- src/widgets/widgets/qlineedit.cpp | 16 ++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/gui/util/qvalidator.cpp b/src/gui/util/qvalidator.cpp index 54cbb28ffa..6e87faf534 100644 --- a/src/gui/util/qvalidator.cpp +++ b/src/gui/util/qvalidator.cpp @@ -370,8 +370,9 @@ QIntValidator::~QIntValidator() \fn QValidator::State QIntValidator::validate(QString &input, int &pos) const Returns \l Acceptable if the \a input is an integer within the - valid range, \l Intermediate if the \a input is a prefix of an integer in the - valid range, and \l Invalid otherwise. + valid range. If \a input has at most as many digits as the top of the range, + or is a prefix of an integer in the valid range, returns \l Intermediate. + Otherwise, returns \l Invalid. If the valid range consists of just positive integers (e.g., 32 to 100) and \a input is a negative integer, then Invalid is returned. (On the other @@ -380,6 +381,10 @@ QIntValidator::~QIntValidator() the user might be just about to type the minus (especially for right-to-left languages). + Similarly, if the valid range is between 46 and 53, then 41 and 59 will be + evaluated as \l Intermediate, as otherwise the user wouldn't be able to + change a value from 49 to 51. + \snippet code/src_gui_util_qvalidator.cpp 2 By default, the \a pos parameter is not used by this validator. diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp index c578710f99..401a62aff3 100644 --- a/src/widgets/widgets/qlineedit.cpp +++ b/src/widgets/widgets/qlineedit.cpp @@ -599,9 +599,17 @@ const QValidator * QLineEdit::validator() const } /*! - Sets this line edit to only accept input that the validator, \a v, - will accept. This allows you to place any arbitrary constraints on - the text which may be entered. + Sets the validator for values of line edit to \a v. + + The line edit's returnPressed() and editingFinished() signals will only + be emitted if \a v validates the line edit's content as \l{QValidator::}{Acceptable}. + The user may change the content to any \l{QValidator::}{Intermediate} + value during editing, but will be prevented from editing the text to a + value that \a v validates as \l{QValidator::}{Invalid}. + + This allows you to constrain the text that shall finally be entered when editing is + done, while leaving users with enough freedom to edit the text from one valid state + to another. If \a v == 0, setValidator() removes the current input validator. The initial setting is to have no input validator (i.e. any input @@ -1439,7 +1447,7 @@ void QLineEdit::copy() const Inserts the clipboard's text at the cursor position, deleting any selected text, providing the line edit is not \l{QLineEdit::readOnly}{read-only}. - If the end result would not be acceptable to the current + If the end result would be invalid to the current \l{setValidator()}{validator}, nothing happens. \sa copy(), cut() -- cgit v1.2.3 From f515ca5dcd02cc7490c3113bdff739aa2b2fd77f Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 1 Apr 2020 10:03:24 +0200 Subject: Fix QTabletEvent manual tests - QPainterPath needs an explicit include now - QTabletEvent::device() is deprecated Change-Id: I2d1086847ee2cf5ed63e345c7c2d6eb43897f0e4 Reviewed-by: Friedemann Kleint --- tests/manual/qtabletevent/device_information/tabletwidget.cpp | 2 +- tests/manual/qtabletevent/regular_widgets/main.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/manual/qtabletevent/device_information/tabletwidget.cpp b/tests/manual/qtabletevent/device_information/tabletwidget.cpp index 14d059abc1..e146175109 100644 --- a/tests/manual/qtabletevent/device_information/tabletwidget.cpp +++ b/tests/manual/qtabletevent/device_information/tabletwidget.cpp @@ -58,7 +58,7 @@ bool TabletWidget::eventFilter(QObject *, QEvent *ev) mPos = event->pos(); mGPos = event->globalPos(); mHiResGlobalPos = event->posF(); - mDev = event->device(); + mDev = event->deviceType(); mPointerType = event->pointerType(); mUnique = event->uniqueId(); mXT = event->xTilt(); diff --git a/tests/manual/qtabletevent/regular_widgets/main.cpp b/tests/manual/qtabletevent/regular_widgets/main.cpp index 4816e2f3b9..1d0af4559b 100644 --- a/tests/manual/qtabletevent/regular_widgets/main.cpp +++ b/tests/manual/qtabletevent/regular_widgets/main.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include enum TabletPointType { -- cgit v1.2.3 From 1430b2944b243b387c9f311c061a5caaf16edea6 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Wed, 1 Apr 2020 14:48:48 +0200 Subject: ANGLE: d3d11: Do not register windows message hooks for d3d11 windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These message hooks are used to handle ALT+ENTER to enter/exit fullscreen mode and PRINTSCREEN to take screenshots. Qt is implementing these functionalities itself so we do not have to register these hooks. If too many of these hooks are registered, callbacks are no longer called and Qt's message queue is no longer handling messages. By saving these hooks we can make sure that more Qt windows at the same time are possible without getting unresponsive due to too many hooks being registered. Change-Id: I5354f91f08cbfeda5e8dc3ad7f824fbd5b3b2932 Reviewed-by: Dominik Holland Reviewed-by: André de la Rocha Reviewed-by: Friedemann Kleint --- .../d3d/d3d11/win32/NativeWindow11Win32.cpp | 4 +- ...-Do-not-register-windows-message-hooks-fo.patch | 45 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 src/angle/patches/0018-ANGLE-d3d11-Do-not-register-windows-message-hooks-fo.patch diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp index 5394e3d3e7..f5e6c93813 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp @@ -168,7 +168,7 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, nullptr, nullptr, &swapChain1); if (SUCCEEDED(result)) { - factory2->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_ALT_ENTER); + factory2->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_WINDOW_CHANGES); *swapChain = static_cast(swapChain1); } SafeRelease(factory2); @@ -196,7 +196,7 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, HRESULT result = factory->CreateSwapChain(device, &swapChainDesc, swapChain); if (SUCCEEDED(result)) { - factory->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_ALT_ENTER); + factory->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_WINDOW_CHANGES); } return result; } diff --git a/src/angle/patches/0018-ANGLE-d3d11-Do-not-register-windows-message-hooks-fo.patch b/src/angle/patches/0018-ANGLE-d3d11-Do-not-register-windows-message-hooks-fo.patch new file mode 100644 index 0000000000..03529c6531 --- /dev/null +++ b/src/angle/patches/0018-ANGLE-d3d11-Do-not-register-windows-message-hooks-fo.patch @@ -0,0 +1,45 @@ +From 3d23de2ad72968d0bf43dac4a9a0f237cc9e03e2 Mon Sep 17 00:00:00 2001 +From: Oliver Wolff +Date: Wed, 1 Apr 2020 14:48:48 +0200 +Subject: [PATCH] ANGLE: d3d11: Do not register windows message hooks for d3d11 + windows + +These message hooks are used to handle ALT+ENTER to enter/exit fullscreen +mode and PRINTSCREEN to take screenshots. Qt is implementing these +functionalities itself so we do not have to register these hooks. + +If too many of these hooks are registered, callbacks are no longer called +and Qt's message queue is no longer handling messages. By saving these +hooks we can make sure that more Qt windows at the same time are possible +without getting unresponsive due to too many hooks being registered. + +Change-Id: I5354f91f08cbfeda5e8dc3ad7f824fbd5b3b2932 +--- + .../src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp +index 5394e3d..f5e6c93 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp +@@ -168,7 +168,7 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, + nullptr, nullptr, &swapChain1); + if (SUCCEEDED(result)) + { +- factory2->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_ALT_ENTER); ++ factory2->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_WINDOW_CHANGES); + *swapChain = static_cast(swapChain1); + } + SafeRelease(factory2); +@@ -196,7 +196,7 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device, + HRESULT result = factory->CreateSwapChain(device, &swapChainDesc, swapChain); + if (SUCCEEDED(result)) + { +- factory->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_ALT_ENTER); ++ factory->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_WINDOW_CHANGES); + } + return result; + } +-- +2.7.4.windows.1 + -- cgit v1.2.3 From 1de427bd4ec9d496061e733b4a849c4927d8888e Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Thu, 2 Apr 2020 12:17:49 +0200 Subject: Use correct deprecation macro, and add \since to new member function Change-Id: Ib9e88855c708f1fe2402d78c55ff08812d86e035 Reviewed-by: Paul Wicking --- src/widgets/dialogs/qwizard.cpp | 4 ++-- src/widgets/dialogs/qwizard.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/widgets/dialogs/qwizard.cpp b/src/widgets/dialogs/qwizard.cpp index b0f4312f40..17ea634efb 100644 --- a/src/widgets/dialogs/qwizard.cpp +++ b/src/widgets/dialogs/qwizard.cpp @@ -2377,11 +2377,11 @@ bool QWizard::hasVisitedPage(int theid) const } /*! + \since 5.15 + Returns the list of IDs of visited pages, in the order in which the pages were visited. - Pressing \uicontrol Back marks the current page as "unvisited" again. - \sa hasVisitedPage() */ QList QWizard::visitedIds() const diff --git a/src/widgets/dialogs/qwizard.h b/src/widgets/dialogs/qwizard.h index dc29c80f51..13b584035c 100644 --- a/src/widgets/dialogs/qwizard.h +++ b/src/widgets/dialogs/qwizard.h @@ -129,7 +129,7 @@ public: QWizardPage *page(int id) const; bool hasVisitedPage(int id) const; #if QT_DEPRECATED_SINCE(5, 15) - Q_DECL_DEPRECATED_X("Use visitedIds() instead") QList visitedPages() const; + QT_DEPRECATED_VERSION_X_5_15("Use visitedIds() instead") QList visitedPages() const; #endif QList visitedIds() const; QList pageIds() const; -- cgit v1.2.3 From 64b1af3fa0e35f673db50273564ac9eab4e5e09c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 3 May 2019 15:13:01 +0200 Subject: Warn that the EDITABLE flag for property declarations is deprecated Additionally mark QMetaProperty::isEditable as deprecated. Change-Id: I1abe4c6f2d30c2f96380f9e5942be431dbfed38f Reviewed-by: Lars Knoll --- src/corelib/kernel/qmetaobject.cpp | 2 ++ src/corelib/kernel/qmetaobject.h | 4 +++- src/tools/moc/moc.cpp | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 1661520b78..62e626b187 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -3599,6 +3599,7 @@ bool QMetaProperty::isRequired() const \sa isDesignable(), isScriptable(), isStored() */ +#if QT_DEPRECATED_SINCE(5, 15) bool QMetaProperty::isEditable(const QObject *object) const { if (!mobj) @@ -3612,6 +3613,7 @@ bool QMetaProperty::isEditable(const QObject *object) const } return b; } +#endif /*! \class QMetaClassInfo diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index beb85becae..e4018740b8 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -262,7 +262,9 @@ public: bool isDesignable(const QObject *obj = nullptr) const; bool isScriptable(const QObject *obj = nullptr) const; bool isStored(const QObject *obj = nullptr) const; - bool isEditable(const QObject *obj = nullptr) const; +#if QT_DEPRECATED_SINCE(5, 15) + QT_DEPRECATED bool isEditable(const QObject *obj = nullptr) const; +#endif bool isUser(const QObject *obj = nullptr) const; bool isConstant() const; bool isFinal() const; diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index c175d1d86d..9dbc22dc2c 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -1296,9 +1296,12 @@ void Moc::createPropertyDef(PropertyDef &propDef) propDef.designable = v + v2; checkIsFunction(propDef.designable, "DESIGNABLE"); break; - case 'E': if (l != "EDITABLE") error(2); + case 'E': if (l != "EDITABLE") error(2); { + const QByteArray msg = "EDITABLE flag for property declaration is deprecated."; + warning(msg.constData()); propDef.editable = v + v2; checkIsFunction(propDef.editable, "EDITABLE"); + } break; case 'N': if (l != "NOTIFY") error(2); propDef.notify = v; -- cgit v1.2.3 From 53ed635dbb5d1ab8103eb1161f81c7e4102d1df2 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 30 Mar 2020 16:15:44 +0200 Subject: Set CONFIG += benchmark in corelib's various benchmarks This leads to "make benchmark" actually running the benchmark, which would be nice, I think. Purged various CONFIG += release or -= debug lines from the same configurations; those surely only configure how the test code is compiled, which is more or less pointless; it's the code under test whose debug/release state matters, and I don't suppose that's affected by the build config of the test code. In the process, reduce diversity of the ordering of lines within these *.pro files and purge some dangling space. Change-Id: Ia9f9f0ca4c096262de928806bdfa6ea3b9e7b9ba Reviewed-by: Joerg Bornemann --- tests/benchmarks/corelib/codecs/qtextcodec/qtextcodec.pro | 5 +++-- tests/benchmarks/corelib/io/qdir/10000/10000.pro | 6 +++--- tests/benchmarks/corelib/io/qdir/tree/tree.pro | 6 +++--- tests/benchmarks/corelib/io/qdiriterator/qdiriterator.pro | 6 ++---- tests/benchmarks/corelib/io/qfile/qfile.pro | 3 ++- tests/benchmarks/corelib/io/qfileinfo/qfileinfo.pro | 6 ++---- tests/benchmarks/corelib/io/qiodevice/qiodevice.pro | 6 ++---- tests/benchmarks/corelib/io/qprocess/test/test.pro | 5 +++-- .../io/qprocess/testProcessLoopback/testProcessLoopback.pro | 4 +++- tests/benchmarks/corelib/io/qtemporaryfile/qtemporaryfile.pro | 6 ++---- tests/benchmarks/corelib/io/qtextstream/qtextstream.pro | 6 ++---- tests/benchmarks/corelib/io/qurl/qurl.pro | 3 ++- tests/benchmarks/corelib/json/json.pro | 3 ++- tests/benchmarks/corelib/kernel/events/events.pro | 4 ++-- .../corelib/kernel/qcoreapplication/qcoreapplication.pro | 4 ++-- tests/benchmarks/corelib/kernel/qmetaobject/qmetaobject.pro | 3 ++- tests/benchmarks/corelib/kernel/qmetatype/qmetatype.pro | 6 +++--- tests/benchmarks/corelib/kernel/qobject/qobject.pro | 4 ++-- .../kernel/qtimer_vs_qmetaobject/qtimer_vs_qmetaobject.pro | 11 ++++------- tests/benchmarks/corelib/kernel/qvariant/qvariant.pro | 7 ++----- .../corelib/mimetypes/qmimedatabase/qmimedatabase.pro | 2 +- tests/benchmarks/corelib/plugin/quuid/quuid.pro | 5 +++-- tests/benchmarks/corelib/text/qbytearray/qbytearray.pro | 6 +++--- tests/benchmarks/corelib/text/qchar/qchar.pro | 4 +++- tests/benchmarks/corelib/text/qlocale/qlocale.pro | 3 ++- tests/benchmarks/corelib/text/qregexp/qregexp.pro | 6 +++--- tests/benchmarks/corelib/text/qstring/qstring.pro | 5 +++-- .../benchmarks/corelib/text/qstringbuilder/qstringbuilder.pro | 10 ++++------ tests/benchmarks/corelib/text/qstringlist/qstringlist.pro | 6 +++--- tests/benchmarks/corelib/thread/qmutex/qmutex.pro | 5 +++-- .../corelib/thread/qreadwritelock/qreadwritelock.pro | 7 ++++--- tests/benchmarks/corelib/thread/qthreadpool/qthreadpool.pro | 5 +++-- .../corelib/thread/qthreadstorage/qthreadstorage.pro | 5 +++-- .../corelib/thread/qwaitcondition/qwaitcondition.pro | 4 +++- tests/benchmarks/corelib/time/qdate/qdate.pro | 3 ++- tests/benchmarks/corelib/time/qdatetime/qdatetime.pro | 3 ++- tests/benchmarks/corelib/time/qtimezone/qtimezone.pro | 3 ++- .../tools/containers-associative/containers-associative.pro | 4 ++-- .../tools/containers-sequential/containers-sequential.pro | 4 ++-- tests/benchmarks/corelib/tools/qalgorithms/qalgorithms.pro | 4 +++- .../corelib/tools/qcontiguouscache/qcontiguouscache.pro | 9 ++++----- .../corelib/tools/qcryptographichash/qcryptographichash.pro | 7 ++++--- tests/benchmarks/corelib/tools/qhash/qhash.pro | 5 +++-- tests/benchmarks/corelib/tools/qlist/qlist.pro | 3 ++- tests/benchmarks/corelib/tools/qmap/qmap.pro | 5 +++-- tests/benchmarks/corelib/tools/qrect/qrect.pro | 5 ++--- tests/benchmarks/corelib/tools/qringbuffer/qringbuffer.pro | 5 ++--- tests/benchmarks/corelib/tools/qset/qset.pro | 5 +++-- tests/benchmarks/corelib/tools/qstack/qstack.pro | 5 +++-- tests/benchmarks/corelib/tools/qvector/qvector.pro | 7 ++++--- 50 files changed, 132 insertions(+), 122 deletions(-) diff --git a/tests/benchmarks/corelib/codecs/qtextcodec/qtextcodec.pro b/tests/benchmarks/corelib/codecs/qtextcodec/qtextcodec.pro index 5ee577c256..7d29c6bfdd 100644 --- a/tests/benchmarks/corelib/codecs/qtextcodec/qtextcodec.pro +++ b/tests/benchmarks/corelib/codecs/qtextcodec/qtextcodec.pro @@ -1,6 +1,7 @@ -TARGET = tst_bench_qtextcodec +CONFIG += benchmark QT = core testlib + +TARGET = tst_bench_qtextcodec SOURCES += main.cpp TESTDATA = utf-8.txt - diff --git a/tests/benchmarks/corelib/io/qdir/10000/10000.pro b/tests/benchmarks/corelib/io/qdir/10000/10000.pro index 2e83dad071..52325f314f 100644 --- a/tests/benchmarks/corelib/io/qdir/10000/10000.pro +++ b/tests/benchmarks/corelib/io/qdir/10000/10000.pro @@ -1,6 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_qdir_10000 +CONFIG += benchmark +QT = core testlib +TARGET = tst_bench_qdir_10000 SOURCES += bench_qdir_10000.cpp - -QT = core testlib diff --git a/tests/benchmarks/corelib/io/qdir/tree/tree.pro b/tests/benchmarks/corelib/io/qdir/tree/tree.pro index 2998a13b57..90ddd23345 100644 --- a/tests/benchmarks/corelib/io/qdir/tree/tree.pro +++ b/tests/benchmarks/corelib/io/qdir/tree/tree.pro @@ -1,7 +1,7 @@ TEMPLATE = app -TARGET = bench_qdir_tree +CONFIG += benchmark +QT = core testlib +TARGET = bench_qdir_tree SOURCES += bench_qdir_tree.cpp RESOURCES += bench_qdir_tree.qrc - -QT = core testlib diff --git a/tests/benchmarks/corelib/io/qdiriterator/qdiriterator.pro b/tests/benchmarks/corelib/io/qdiriterator/qdiriterator.pro index 061b22a5d1..b332cda84b 100644 --- a/tests/benchmarks/corelib/io/qdiriterator/qdiriterator.pro +++ b/tests/benchmarks/corelib/io/qdiriterator/qdiriterator.pro @@ -1,8 +1,6 @@ -TARGET = tst_bench_qdiriterator - +CONFIG += benchmark QT = core testlib -CONFIG += release - +TARGET = tst_bench_qdiriterator SOURCES += main.cpp qfilesystemiterator.cpp HEADERS += qfilesystemiterator.h diff --git a/tests/benchmarks/corelib/io/qfile/qfile.pro b/tests/benchmarks/corelib/io/qfile/qfile.pro index 5f7b9af73f..a882c4ea61 100644 --- a/tests/benchmarks/corelib/io/qfile/qfile.pro +++ b/tests/benchmarks/corelib/io/qfile/qfile.pro @@ -1,6 +1,7 @@ TEMPLATE = app -TARGET = tst_bench_qfile +CONFIG += benchmark QT = core core-private testlib win32: DEFINES+= _CRT_SECURE_NO_WARNINGS +TARGET = tst_bench_qfile SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/io/qfileinfo/qfileinfo.pro b/tests/benchmarks/corelib/io/qfileinfo/qfileinfo.pro index 42e8708b02..9c97bfc84a 100644 --- a/tests/benchmarks/corelib/io/qfileinfo/qfileinfo.pro +++ b/tests/benchmarks/corelib/io/qfileinfo/qfileinfo.pro @@ -1,9 +1,7 @@ TEMPLATE = app -TARGET = tst_bench_qfileinfo - +CONFIG += benchmark QT -= gui QT += core-private testlib -CONFIG += release - +TARGET = tst_bench_qfileinfo SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/io/qiodevice/qiodevice.pro b/tests/benchmarks/corelib/io/qiodevice/qiodevice.pro index 7937436e13..febe6e87f9 100644 --- a/tests/benchmarks/corelib/io/qiodevice/qiodevice.pro +++ b/tests/benchmarks/corelib/io/qiodevice/qiodevice.pro @@ -1,8 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_qiodevice - +CONFIG += benchmark QT = core testlib -CONFIG += release - +TARGET = tst_bench_qiodevice SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/io/qprocess/test/test.pro b/tests/benchmarks/corelib/io/qprocess/test/test.pro index b665525b17..e7e8e01aef 100644 --- a/tests/benchmarks/corelib/io/qprocess/test/test.pro +++ b/tests/benchmarks/corelib/io/qprocess/test/test.pro @@ -1,4 +1,5 @@ +CONFIG += benchmark +QT = core core-private testlib + TARGET = ../tst_bench_qprocess SOURCES += ../tst_bench_qprocess.cpp - -QT = core core-private testlib diff --git a/tests/benchmarks/corelib/io/qprocess/testProcessLoopback/testProcessLoopback.pro b/tests/benchmarks/corelib/io/qprocess/testProcessLoopback/testProcessLoopback.pro index a0230e1cb8..1f56ad6ee6 100644 --- a/tests/benchmarks/corelib/io/qprocess/testProcessLoopback/testProcessLoopback.pro +++ b/tests/benchmarks/corelib/io/qprocess/testProcessLoopback/testProcessLoopback.pro @@ -1,5 +1,7 @@ -SOURCES = main.cpp +CONFIG += benchmark CONFIG -= qt CONFIG += cmdline winrt: QMAKE_LFLAGS += /ENTRY:mainCRTStartup + +SOURCES = main.cpp DESTDIR = ./ diff --git a/tests/benchmarks/corelib/io/qtemporaryfile/qtemporaryfile.pro b/tests/benchmarks/corelib/io/qtemporaryfile/qtemporaryfile.pro index 758930c139..b6064e1f91 100644 --- a/tests/benchmarks/corelib/io/qtemporaryfile/qtemporaryfile.pro +++ b/tests/benchmarks/corelib/io/qtemporaryfile/qtemporaryfile.pro @@ -1,8 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_qtemporaryfile - +CONFIG += benchmark QT = core testlib -CONFIG += release - +TARGET = tst_bench_qtemporaryfile SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/io/qtextstream/qtextstream.pro b/tests/benchmarks/corelib/io/qtextstream/qtextstream.pro index e8170319f2..fb45d05bc9 100644 --- a/tests/benchmarks/corelib/io/qtextstream/qtextstream.pro +++ b/tests/benchmarks/corelib/io/qtextstream/qtextstream.pro @@ -1,8 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_qtextstream - +CONFIG += benchmark QT = core testlib -CONFIG += release - +TARGET = tst_bench_qtextstream SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/io/qurl/qurl.pro b/tests/benchmarks/corelib/io/qurl/qurl.pro index 52f7bdc8b6..0e10e32a22 100644 --- a/tests/benchmarks/corelib/io/qurl/qurl.pro +++ b/tests/benchmarks/corelib/io/qurl/qurl.pro @@ -1,6 +1,7 @@ TEMPLATE = app -TARGET = tst_qurl +CONFIG += benchmark QT = core testlib win32: DEFINES+= _CRT_SECURE_NO_WARNINGS +TARGET = tst_qurl SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/json/json.pro b/tests/benchmarks/corelib/json/json.pro index 004f4b123e..8f9e515cb9 100644 --- a/tests/benchmarks/corelib/json/json.pro +++ b/tests/benchmarks/corelib/json/json.pro @@ -1,7 +1,8 @@ -TARGET = tst_bench_qtbinaryjson QT = core testlib +CONFIG += benchmark CONFIG -= app_bundle +TARGET = tst_bench_qtbinaryjson SOURCES += tst_bench_qtbinaryjson.cpp TESTDATA = numbers.json test.json diff --git a/tests/benchmarks/corelib/kernel/events/events.pro b/tests/benchmarks/corelib/kernel/events/events.pro index 798a880e5b..1381bb001e 100644 --- a/tests/benchmarks/corelib/kernel/events/events.pro +++ b/tests/benchmarks/corelib/kernel/events/events.pro @@ -1,6 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_events - +CONFIG += benchmark QT = core testlib +TARGET = tst_bench_events SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/kernel/qcoreapplication/qcoreapplication.pro b/tests/benchmarks/corelib/kernel/qcoreapplication/qcoreapplication.pro index 8bf8487a5f..5572f06924 100644 --- a/tests/benchmarks/corelib/kernel/qcoreapplication/qcoreapplication.pro +++ b/tests/benchmarks/corelib/kernel/qcoreapplication/qcoreapplication.pro @@ -1,6 +1,6 @@ +TEMPLATE = app +CONFIG += benchmark QT = core testlib -TEMPLATE = app TARGET = tst_bench_qcoreapplication - SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/kernel/qmetaobject/qmetaobject.pro b/tests/benchmarks/corelib/kernel/qmetaobject/qmetaobject.pro index 47d2acb9b1..0d595ed4da 100644 --- a/tests/benchmarks/corelib/kernel/qmetaobject/qmetaobject.pro +++ b/tests/benchmarks/corelib/kernel/qmetaobject/qmetaobject.pro @@ -1,5 +1,6 @@ TEMPLATE = app +CONFIG += benchmark QT += widgets testlib -TARGET = tst_bench_qmetaobject +TARGET = tst_bench_qmetaobject SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/kernel/qmetatype/qmetatype.pro b/tests/benchmarks/corelib/kernel/qmetatype/qmetatype.pro index 83d0708b60..ffd36ad202 100644 --- a/tests/benchmarks/corelib/kernel/qmetatype/qmetatype.pro +++ b/tests/benchmarks/corelib/kernel/qmetatype/qmetatype.pro @@ -1,6 +1,6 @@ -QT = core testlib TEMPLATE = app -TARGET = tst_bench_qmetatype +CONFIG += benchmark +QT = core testlib +TARGET = tst_bench_qmetatype SOURCES += tst_qmetatype.cpp - diff --git a/tests/benchmarks/corelib/kernel/qobject/qobject.pro b/tests/benchmarks/corelib/kernel/qobject/qobject.pro index e611eff0a2..eb1d8a2daa 100644 --- a/tests/benchmarks/corelib/kernel/qobject/qobject.pro +++ b/tests/benchmarks/corelib/kernel/qobject/qobject.pro @@ -1,7 +1,7 @@ +TEMPLATE = app +CONFIG += benchmark QT += widgets testlib -TEMPLATE = app TARGET = tst_bench_qobject - HEADERS += object.h SOURCES += main.cpp object.cpp diff --git a/tests/benchmarks/corelib/kernel/qtimer_vs_qmetaobject/qtimer_vs_qmetaobject.pro b/tests/benchmarks/corelib/kernel/qtimer_vs_qmetaobject/qtimer_vs_qmetaobject.pro index e127ba1934..3d4e48e76c 100644 --- a/tests/benchmarks/corelib/kernel/qtimer_vs_qmetaobject/qtimer_vs_qmetaobject.pro +++ b/tests/benchmarks/corelib/kernel/qtimer_vs_qmetaobject/qtimer_vs_qmetaobject.pro @@ -1,10 +1,7 @@ TEMPLATE = app -TARGET = qtimer_vs_qmetaobject -INCLUDEPATH += . - -CONFIG += release -#CONFIG += debug - +CONFIG += benchmark +QT = core testlib +INCLUDEPATH += . +TARGET = qtimer_vs_qmetaobject SOURCES += tst_qtimer_vs_qmetaobject.cpp -QT = core testlib diff --git a/tests/benchmarks/corelib/kernel/qvariant/qvariant.pro b/tests/benchmarks/corelib/kernel/qvariant/qvariant.pro index 8a8e9f25d3..2616ae78ea 100644 --- a/tests/benchmarks/corelib/kernel/qvariant/qvariant.pro +++ b/tests/benchmarks/corelib/kernel/qvariant/qvariant.pro @@ -1,9 +1,6 @@ -TARGET = tst_bench_qvariant +CONFIG += benchmark QT += testlib !qtHaveModule(gui): QT -= gui -CONFIG += release -#CONFIG += debug - - +TARGET = tst_bench_qvariant SOURCES += tst_qvariant.cpp diff --git a/tests/benchmarks/corelib/mimetypes/qmimedatabase/qmimedatabase.pro b/tests/benchmarks/corelib/mimetypes/qmimedatabase/qmimedatabase.pro index fe55b98e54..3d218554d3 100644 --- a/tests/benchmarks/corelib/mimetypes/qmimedatabase/qmimedatabase.pro +++ b/tests/benchmarks/corelib/mimetypes/qmimedatabase/qmimedatabase.pro @@ -1,5 +1,5 @@ +CONFIG += benchmark QT = core testlib TARGET = tst_bench_qmimedatabase SOURCES = main.cpp - diff --git a/tests/benchmarks/corelib/plugin/quuid/quuid.pro b/tests/benchmarks/corelib/plugin/quuid/quuid.pro index 8f88bb85b4..5179c0cc40 100644 --- a/tests/benchmarks/corelib/plugin/quuid/quuid.pro +++ b/tests/benchmarks/corelib/plugin/quuid/quuid.pro @@ -1,5 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_quuid +CONFIG += benchmark +QT = core testlib +TARGET = tst_bench_quuid SOURCES += tst_quuid.cpp -QT = core testlib diff --git a/tests/benchmarks/corelib/text/qbytearray/qbytearray.pro b/tests/benchmarks/corelib/text/qbytearray/qbytearray.pro index cf28b0247f..25af9512d4 100644 --- a/tests/benchmarks/corelib/text/qbytearray/qbytearray.pro +++ b/tests/benchmarks/corelib/text/qbytearray/qbytearray.pro @@ -1,7 +1,7 @@ TEMPLATE = app -TARGET = tst_bench_qbytearray - +CONFIG += benchmark QT = core testlib -TESTDATA += main.cpp +TARGET = tst_bench_qbytearray SOURCES += main.cpp +TESTDATA += main.cpp diff --git a/tests/benchmarks/corelib/text/qchar/qchar.pro b/tests/benchmarks/corelib/text/qchar/qchar.pro index 80a9861f48..902acbb831 100644 --- a/tests/benchmarks/corelib/text/qchar/qchar.pro +++ b/tests/benchmarks/corelib/text/qchar/qchar.pro @@ -1,3 +1,5 @@ -TARGET = tst_bench_qchar +CONFIG += benchmark QT = core testlib + +TARGET = tst_bench_qchar SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/text/qlocale/qlocale.pro b/tests/benchmarks/corelib/text/qlocale/qlocale.pro index e56bbe0341..a39a20a677 100644 --- a/tests/benchmarks/corelib/text/qlocale/qlocale.pro +++ b/tests/benchmarks/corelib/text/qlocale/qlocale.pro @@ -1,4 +1,5 @@ -TARGET = tst_bench_qlocale +CONFIG += benchmark QT = core testlib +TARGET = tst_bench_qlocale SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/text/qregexp/qregexp.pro b/tests/benchmarks/corelib/text/qregexp/qregexp.pro index f64ae781a2..c04c13060b 100644 --- a/tests/benchmarks/corelib/text/qregexp/qregexp.pro +++ b/tests/benchmarks/corelib/text/qregexp/qregexp.pro @@ -1,8 +1,9 @@ TEMPLATE = app -TARGET = tst_bench_qregexp +CONFIG += benchmark +CONFIG += exceptions QT = core testlib -CONFIG += release exceptions +TARGET = tst_bench_qregexp SOURCES += main.cpp RESOURCES += qregexp.qrc @@ -17,4 +18,3 @@ qtHaveModule(script):!pcre { LIBS += -lboost_regex } } - diff --git a/tests/benchmarks/corelib/text/qstring/qstring.pro b/tests/benchmarks/corelib/text/qstring/qstring.pro index 9f5e34b915..e25431b983 100644 --- a/tests/benchmarks/corelib/text/qstring/qstring.pro +++ b/tests/benchmarks/corelib/text/qstring/qstring.pro @@ -1,5 +1,6 @@ -TARGET = tst_bench_qstring +CONFIG += benchmark QT -= gui QT += core testlib -SOURCES += main.cpp +TARGET = tst_bench_qstring +SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/text/qstringbuilder/qstringbuilder.pro b/tests/benchmarks/corelib/text/qstringbuilder/qstringbuilder.pro index fa4cbe3c13..91421b3b2c 100644 --- a/tests/benchmarks/corelib/text/qstringbuilder/qstringbuilder.pro +++ b/tests/benchmarks/corelib/text/qstringbuilder/qstringbuilder.pro @@ -1,11 +1,9 @@ TEMPLATE = app -TARGET = tst_bench_qstringbuilder +CONFIG += benchmark +QT = core testlib QMAKE_CXXFLAGS += -g QMAKE_CFLAGS += -g -QT = core testlib - -CONFIG += release - -SOURCES += main.cpp +TARGET = tst_bench_qstringbuilder +SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/text/qstringlist/qstringlist.pro b/tests/benchmarks/corelib/text/qstringlist/qstringlist.pro index 5803e7da0e..a27bf0a6ab 100644 --- a/tests/benchmarks/corelib/text/qstringlist/qstringlist.pro +++ b/tests/benchmarks/corelib/text/qstringlist/qstringlist.pro @@ -1,5 +1,5 @@ -TARGET = tst_bench_qstringlist -CONFIG -= debug -CONFIG += release +CONFIG += benchmark QT = core testlib + +TARGET = tst_bench_qstringlist SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/thread/qmutex/qmutex.pro b/tests/benchmarks/corelib/thread/qmutex/qmutex.pro index ec87f60919..a0b2ddeaa9 100644 --- a/tests/benchmarks/corelib/thread/qmutex/qmutex.pro +++ b/tests/benchmarks/corelib/thread/qmutex/qmutex.pro @@ -1,5 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_qmutex +CONFIG += benchmark QT = core testlib -SOURCES += tst_qmutex.cpp +TARGET = tst_bench_qmutex +SOURCES += tst_qmutex.cpp diff --git a/tests/benchmarks/corelib/thread/qreadwritelock/qreadwritelock.pro b/tests/benchmarks/corelib/thread/qreadwritelock/qreadwritelock.pro index a1827d0276..7c36067cb7 100644 --- a/tests/benchmarks/corelib/thread/qreadwritelock/qreadwritelock.pro +++ b/tests/benchmarks/corelib/thread/qreadwritelock/qreadwritelock.pro @@ -1,7 +1,8 @@ TEMPLATE = app -TARGET = tst_bench_qreadwritelock -QT = core-private testlib -SOURCES += tst_qreadwritelock.cpp +CONFIG += benchmark CONFIG += c++14 # for std::shared_timed_mutex CONFIG += c++1z # for std::shared_mutex +QT = core-private testlib +TARGET = tst_bench_qreadwritelock +SOURCES += tst_qreadwritelock.cpp diff --git a/tests/benchmarks/corelib/thread/qthreadpool/qthreadpool.pro b/tests/benchmarks/corelib/thread/qthreadpool/qthreadpool.pro index 47e16e8b4d..303b3cef69 100644 --- a/tests/benchmarks/corelib/thread/qthreadpool/qthreadpool.pro +++ b/tests/benchmarks/corelib/thread/qthreadpool/qthreadpool.pro @@ -1,5 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_qthreadpool +CONFIG += benchmark +QT = core testlib +TARGET = tst_bench_qthreadpool SOURCES += tst_qthreadpool.cpp -QT = core testlib diff --git a/tests/benchmarks/corelib/thread/qthreadstorage/qthreadstorage.pro b/tests/benchmarks/corelib/thread/qthreadstorage/qthreadstorage.pro index 95afc951bc..3f62c4eb3c 100644 --- a/tests/benchmarks/corelib/thread/qthreadstorage/qthreadstorage.pro +++ b/tests/benchmarks/corelib/thread/qthreadstorage/qthreadstorage.pro @@ -1,5 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_qthreadstorage +CONFIG += benchmark +QT = core testlib +TARGET = tst_bench_qthreadstorage SOURCES += tst_qthreadstorage.cpp -QT = core testlib diff --git a/tests/benchmarks/corelib/thread/qwaitcondition/qwaitcondition.pro b/tests/benchmarks/corelib/thread/qwaitcondition/qwaitcondition.pro index 43c7921a93..cc801bdc13 100644 --- a/tests/benchmarks/corelib/thread/qwaitcondition/qwaitcondition.pro +++ b/tests/benchmarks/corelib/thread/qwaitcondition/qwaitcondition.pro @@ -1,4 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_qwaitcondition +CONFIG += benchmark QT = core testlib + +TARGET = tst_bench_qwaitcondition SOURCES += tst_qwaitcondition.cpp diff --git a/tests/benchmarks/corelib/time/qdate/qdate.pro b/tests/benchmarks/corelib/time/qdate/qdate.pro index a655917135..ecb229dfda 100644 --- a/tests/benchmarks/corelib/time/qdate/qdate.pro +++ b/tests/benchmarks/corelib/time/qdate/qdate.pro @@ -1,4 +1,5 @@ -TARGET = tst_bench_qdate +CONFIG += benchmark QT = core testlib +TARGET = tst_bench_qdate SOURCES += tst_bench_qdate.cpp diff --git a/tests/benchmarks/corelib/time/qdatetime/qdatetime.pro b/tests/benchmarks/corelib/time/qdatetime/qdatetime.pro index a85e7346c6..7133834ffc 100644 --- a/tests/benchmarks/corelib/time/qdatetime/qdatetime.pro +++ b/tests/benchmarks/corelib/time/qdatetime/qdatetime.pro @@ -1,4 +1,5 @@ -TARGET = tst_bench_qdatetime +CONFIG += benchmark QT = core testlib +TARGET = tst_bench_qdatetime SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/time/qtimezone/qtimezone.pro b/tests/benchmarks/corelib/time/qtimezone/qtimezone.pro index d0531b568b..6ebee0faf3 100644 --- a/tests/benchmarks/corelib/time/qtimezone/qtimezone.pro +++ b/tests/benchmarks/corelib/time/qtimezone/qtimezone.pro @@ -1,4 +1,5 @@ -TARGET = tst_bench_qtimezone +CONFIG += benchmark QT = core testlib +TARGET = tst_bench_qtimezone SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/tools/containers-associative/containers-associative.pro b/tests/benchmarks/corelib/tools/containers-associative/containers-associative.pro index 49edcbee70..89da01b02e 100644 --- a/tests/benchmarks/corelib/tools/containers-associative/containers-associative.pro +++ b/tests/benchmarks/corelib/tools/containers-associative/containers-associative.pro @@ -1,6 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_containers-associative - +CONFIG += benchmark QT = core testlib +TARGET = tst_bench_containers-associative SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/tools/containers-sequential/containers-sequential.pro b/tests/benchmarks/corelib/tools/containers-sequential/containers-sequential.pro index 6f731cb6d8..509da95d22 100644 --- a/tests/benchmarks/corelib/tools/containers-sequential/containers-sequential.pro +++ b/tests/benchmarks/corelib/tools/containers-sequential/containers-sequential.pro @@ -1,6 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_containers-sequential - +CONFIG += benchmark QT = core testlib +TARGET = tst_bench_containers-sequential SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/tools/qalgorithms/qalgorithms.pro b/tests/benchmarks/corelib/tools/qalgorithms/qalgorithms.pro index f54f8320d4..0bde3ac66a 100644 --- a/tests/benchmarks/corelib/tools/qalgorithms/qalgorithms.pro +++ b/tests/benchmarks/corelib/tools/qalgorithms/qalgorithms.pro @@ -1,3 +1,5 @@ -TARGET = tst_bench_qalgorithms +CONFIG += benchmark QT = core testlib + +TARGET = tst_bench_qalgorithms SOURCES = tst_qalgorithms.cpp diff --git a/tests/benchmarks/corelib/tools/qcontiguouscache/qcontiguouscache.pro b/tests/benchmarks/corelib/tools/qcontiguouscache/qcontiguouscache.pro index fe74dafef4..59adad6bbc 100644 --- a/tests/benchmarks/corelib/tools/qcontiguouscache/qcontiguouscache.pro +++ b/tests/benchmarks/corelib/tools/qcontiguouscache/qcontiguouscache.pro @@ -1,7 +1,6 @@ -TARGET = tst_bench_qcontiguouscache - -SOURCES += main.cpp - +CONFIG += benchmark CONFIG += parallel_test - QT = core testlib + +TARGET = tst_bench_qcontiguouscache +SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/tools/qcryptographichash/qcryptographichash.pro b/tests/benchmarks/corelib/tools/qcryptographichash/qcryptographichash.pro index cf9d640f7e..025c70c2d9 100644 --- a/tests/benchmarks/corelib/tools/qcryptographichash/qcryptographichash.pro +++ b/tests/benchmarks/corelib/tools/qcryptographichash/qcryptographichash.pro @@ -1,5 +1,6 @@ -TARGET = tst_bench_qcryptographichash -CONFIG -= debug -CONFIG += release cmdline +CONFIG += benchmark +CONFIG += cmdline QT = core testlib + +TARGET = tst_bench_qcryptographichash SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/tools/qhash/qhash.pro b/tests/benchmarks/corelib/tools/qhash/qhash.pro index 40f661c116..f9a873d096 100644 --- a/tests/benchmarks/corelib/tools/qhash/qhash.pro +++ b/tests/benchmarks/corelib/tools/qhash/qhash.pro @@ -1,5 +1,6 @@ -TARGET = tst_hash +CONFIG += benchmark QT = core testlib + INCLUDEPATH += . +TARGET = tst_hash SOURCES += main.cpp outofline.cpp -CONFIG += release diff --git a/tests/benchmarks/corelib/tools/qlist/qlist.pro b/tests/benchmarks/corelib/tools/qlist/qlist.pro index c83bc455d2..98767c3250 100644 --- a/tests/benchmarks/corelib/tools/qlist/qlist.pro +++ b/tests/benchmarks/corelib/tools/qlist/qlist.pro @@ -1,4 +1,5 @@ -TARGET = tst_bench_qlist +CONFIG += benchmark QT = core testlib +TARGET = tst_bench_qlist SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/tools/qmap/qmap.pro b/tests/benchmarks/corelib/tools/qmap/qmap.pro index 6c9bf5e8d6..0e06493c79 100644 --- a/tests/benchmarks/corelib/tools/qmap/qmap.pro +++ b/tests/benchmarks/corelib/tools/qmap/qmap.pro @@ -1,5 +1,6 @@ -TARGET = tst_bench_qmap +CONFIG += benchmark QT = core testlib + INCLUDEPATH += . +TARGET = tst_bench_qmap SOURCES += main.cpp -CONFIG += release diff --git a/tests/benchmarks/corelib/tools/qrect/qrect.pro b/tests/benchmarks/corelib/tools/qrect/qrect.pro index 42cfcd8924..211cdc5bcc 100644 --- a/tests/benchmarks/corelib/tools/qrect/qrect.pro +++ b/tests/benchmarks/corelib/tools/qrect/qrect.pro @@ -1,7 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_qrect - QT = core testlib -CONFIG += release +CONFIG += benchmark +TARGET = tst_bench_qrect SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/tools/qringbuffer/qringbuffer.pro b/tests/benchmarks/corelib/tools/qringbuffer/qringbuffer.pro index 21b50e10e5..69750865b5 100644 --- a/tests/benchmarks/corelib/tools/qringbuffer/qringbuffer.pro +++ b/tests/benchmarks/corelib/tools/qringbuffer/qringbuffer.pro @@ -1,7 +1,6 @@ TEMPLATE = app -TARGET = tst_bench_qringbuffer - +CONFIG += benchmark QT = core-private testlib -CONFIG += release +TARGET = tst_bench_qringbuffer SOURCES += main.cpp diff --git a/tests/benchmarks/corelib/tools/qset/qset.pro b/tests/benchmarks/corelib/tools/qset/qset.pro index 8fb8bcfa0b..e448683e94 100644 --- a/tests/benchmarks/corelib/tools/qset/qset.pro +++ b/tests/benchmarks/corelib/tools/qset/qset.pro @@ -1,4 +1,5 @@ -TARGET = tst_qset +CONFIG += benchmark QT = core testlib + +TARGET = tst_qset SOURCES += main.cpp -CONFIG += release diff --git a/tests/benchmarks/corelib/tools/qstack/qstack.pro b/tests/benchmarks/corelib/tools/qstack/qstack.pro index 7d8a839610..17b7ebd486 100644 --- a/tests/benchmarks/corelib/tools/qstack/qstack.pro +++ b/tests/benchmarks/corelib/tools/qstack/qstack.pro @@ -1,4 +1,5 @@ -TARGET = tst_bench_stack +CONFIG += benchmark QT = core testlib core-private + +TARGET = tst_bench_stack SOURCES += main.cpp -CONFIG += release diff --git a/tests/benchmarks/corelib/tools/qvector/qvector.pro b/tests/benchmarks/corelib/tools/qvector/qvector.pro index 24a65d8ee8..fce8a6cd78 100644 --- a/tests/benchmarks/corelib/tools/qvector/qvector.pro +++ b/tests/benchmarks/corelib/tools/qvector/qvector.pro @@ -1,5 +1,6 @@ -TARGET = tst_bench_vector +CONFIG += benchmark QT = core testlib core-private + INCLUDEPATH += . -SOURCES += main.cpp outofline.cpp -CONFIG += release +TARGET = tst_bench_vector +SOURCES += main.cpp outofline.cpp -- cgit v1.2.3 From 6387138a7991b4588639dc48847f175b5afaff84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 27 Mar 2020 11:53:35 +0100 Subject: Pass SDK root to the linker as -isysroot, not -Wl,-syslibroot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The former option to clang will result in more options to the linker, such as the newly introduced -platform_version, which writes the SDK version to the resulting binary. By using the syslibroot flag directly we were missing the platform version, and binaries were left without an SDK version set, resulting in failed validation of the binary. Going with the clang driver gives us the right behavior for free. Fixes: QTBUG-83100 Change-Id: I98bc9ba644dae4bcc7a6a88481556bae185ce5fa Reviewed-by: Simon Hausmann Reviewed-by: Timur Pocheptsov (cherry picked from commit 6a60192ac03d0b4ab542191065122243cebcd1ca) Reviewed-by: Tor Arne Vestbø --- configure | 5 +---- mkspecs/features/mac/default_post.prf | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/configure b/configure index 6657870e4b..b6c9b462f2 100755 --- a/configure +++ b/configure @@ -271,12 +271,9 @@ macSDKify() val=$(echo $sdk_val $(echo $val | cut -s -d ' ' -f 2-)) echo "$var=$val" ;; - QMAKE_CFLAGS=*|QMAKE_CXXFLAGS=*) + QMAKE_CFLAGS=*|QMAKE_CXXFLAGS=*|QMAKE_LFLAGS=*) echo "$line -isysroot $sysroot $version_min_flag" ;; - QMAKE_LFLAGS=*) - echo "$line -Wl,-syslibroot,$sysroot $version_min_flag" - ;; *) echo "$line" ;; diff --git a/mkspecs/features/mac/default_post.prf b/mkspecs/features/mac/default_post.prf index ba163efc18..92a9112bca 100644 --- a/mkspecs/features/mac/default_post.prf +++ b/mkspecs/features/mac/default_post.prf @@ -198,7 +198,7 @@ macx-xcode { -isysroot$$xcodeSDKInfo(Path, $$sdk) QMAKE_XARCH_LFLAGS_$${arch} = $$version_min_flags \ -Xarch_$${arch} \ - -Wl,-syslibroot,$$xcodeSDKInfo(Path, $$sdk) + -isysroot$$xcodeSDKInfo(Path, $$sdk) QMAKE_XARCH_CFLAGS += $(EXPORT_QMAKE_XARCH_CFLAGS_$${arch}) QMAKE_XARCH_LFLAGS += $(EXPORT_QMAKE_XARCH_LFLAGS_$${arch}) @@ -222,7 +222,7 @@ macx-xcode { version_min_flag = -m$${version_identifier}-version-min=$$deployment_target QMAKE_CFLAGS += -isysroot $$sysroot_path $$version_min_flag QMAKE_CXXFLAGS += -isysroot $$sysroot_path $$version_min_flag - QMAKE_LFLAGS += -Wl,-syslibroot,$$sysroot_path $$version_min_flag + QMAKE_LFLAGS += -isysroot $$sysroot_path $$version_min_flag } # Enable precompiled headers for multiple architectures -- cgit v1.2.3 From 54886d7f81175ac6bc39a0b40efd18c886b8bf8f Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sun, 12 Jan 2020 20:55:11 +0100 Subject: Enable accessibility on Linux when org.a11y.Status IsEnable is true Otherwise accessibility would only work when Orca is set to be started in the session preference, and it would not work when running Orca or compiz' zoom by hand. The existing comment said that it was always true since gnome 3.6, but at least in Debian 8's gnome 3.14, Debian 9's gnome 3.22, and Debian 10's 3.30 it is not always true, it is Orca which sets it on startup. Compiz's focuspoll module also does so for people with low vision using zoom with focus tracking. [ChangeLog][Accessibility][Linux] Enable accessibility on Linux when Orca is started by hand Change-Id: I36cfe1b45e442c0fcefe813e09a67a74205c3ecf Reviewed-by: Frederik Gladhorn --- src/platformsupport/linuxaccessibility/dbusconnection.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/platformsupport/linuxaccessibility/dbusconnection.cpp b/src/platformsupport/linuxaccessibility/dbusconnection.cpp index cacbfdae9f..45ddc8e496 100644 --- a/src/platformsupport/linuxaccessibility/dbusconnection.cpp +++ b/src/platformsupport/linuxaccessibility/dbusconnection.cpp @@ -120,8 +120,7 @@ void DBusConnection::serviceRegistered() //debugging. static const bool a11yAlwaysOn = qEnvironmentVariableIsSet("QT_LINUX_ACCESSIBILITY_ALWAYS_ON"); - // a11yStatus->isEnabled() returns always true (since Gnome 3.6) - bool enabled = a11yAlwaysOn || a11yStatus->screenReaderEnabled(); + bool enabled = a11yAlwaysOn || a11yStatus->screenReaderEnabled() || a11yStatus->isEnabled(); if (enabled != m_enabled) { m_enabled = enabled; -- cgit v1.2.3 From 333d7e6de434fbe7777f948761ac5e7b5065d83f Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Tue, 31 Mar 2020 17:17:13 +0200 Subject: Clean up QTzTimeZonePrivate::systemTimeZoneId() The special handling of ":/etc/localtime" should only apply if that's the exact value of $TZ; the old code would have treated "/etc/localtime" the same, due to stripping a leading ':' before checking for it. We can also test whether to do that stripping using startsWith(). When reading the content of files, avoid QTextStream's trip via QString and back to QByteArray by using the QFile's readLine() directly, or by using readAll(). Task-number: QTBUG-75585 Change-Id: I1524529a2c34d83a9fbd00d41c11f2d994dfc49d Reviewed-by: Ulf Hermann --- src/corelib/time/qtimezoneprivate_tz.cpp | 33 +++++++++++++------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp index a94ed6b7a9..bf6963a441 100644 --- a/src/corelib/time/qtimezoneprivate_tz.cpp +++ b/src/corelib/time/qtimezoneprivate_tz.cpp @@ -1123,15 +1123,15 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const { // Check TZ env var first, if not populated try find it QByteArray ianaId = qgetenv("TZ"); - if (!ianaId.isEmpty() && ianaId.at(0) == ':') - ianaId = ianaId.mid(1); // The TZ value can be ":/etc/localtime" which libc considers // to be a "default timezone", in which case it will be read // by one of the blocks below, so unset it here so it is not // considered as a valid/found ianaId - if (ianaId == "/etc/localtime") + if (ianaId == ":/etc/localtime") ianaId.clear(); + else if (ianaId.startsWith(':')) + ianaId = ianaId.mid(1); // On most distros /etc/localtime is a symlink to a real file so extract name from the path if (ianaId.isEmpty()) { @@ -1150,15 +1150,12 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const } } - // On Debian Etch up to Jessie, /etc/localtime is a regular file while the actual name is in /etc/timezone + // On Debian Etch up to Jessie, /etc/localtime is a copy of the relevant + // zoneinfo file, whose name is recorded in /etc/timezone: if (ianaId.isEmpty()) { QFile tzif(QStringLiteral("/etc/timezone")); - if (tzif.open(QIODevice::ReadOnly)) { - // TODO QTextStream inefficient, replace later - QTextStream ts(&tzif); - if (!ts.atEnd()) - ianaId = ts.readLine().toUtf8(); - } + if (tzif.open(QIODevice::ReadOnly)) + ianaId = tzif.readAll().trimmed(); } // On some Red Hat distros /etc/localtime is real file with name held in /etc/sysconfig/clock @@ -1166,16 +1163,12 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const if (ianaId.isEmpty()) { QFile tzif(QStringLiteral("/etc/sysconfig/clock")); if (tzif.open(QIODevice::ReadOnly)) { - // TODO QTextStream inefficient, replace later - QTextStream ts(&tzif); - QString line; - while (ianaId.isEmpty() && !ts.atEnd() && ts.status() == QTextStream::Ok) { - line = ts.readLine(); - if (line.startsWith(QLatin1String("ZONE="))) { - ianaId = line.midRef(6, line.size() - 7).toUtf8(); - } else if (line.startsWith(QLatin1String("TIMEZONE="))) { - ianaId = line.midRef(10, line.size() - 11).toUtf8(); - } + while (ianaId.isEmpty() && !tzif.atEnd()) { + const QByteArray line(tzif.readLine().trimmed()); + if (line.startsWith("ZONE=")) + ianaId = line.mid(6, line.length() - 7); + else if (line.startsWith("TIMEZONE=")) + ianaId = line.mid(10, line.length() - 11); } } } -- cgit v1.2.3 From a20697a3940ede60b2fd5eac0ffd1a57b132191a Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 19 Feb 2020 15:17:16 +0100 Subject: Rework cldr2qlocalexml.py in terms of a QLocaleXmlWriter class Delegate the output of XML to a helper class provided by qlocalexml.py and restructure the driver script so that it can be imported without running anything. It now has a minimal __name__ == '__main__' block that calls a main() function. This, for the moment, requires a global via which it shares the CLDR directory with various other functions; that shall go away in a later commit. Task-number: QTBUG-81344 Change-Id: Ica2d3ec09f2d38ba42fd930258cc765283f29a71 Reviewed-by: Cristian Maureira-Fredes --- util/locale_database/cldr2qlocalexml.py | 312 ++++++++++++++------------------ util/locale_database/qlocalexml.py | 215 +++++++++++++++++++--- 2 files changed, 330 insertions(+), 197 deletions(-) diff --git a/util/locale_database/cldr2qlocalexml.py b/util/locale_database/cldr2qlocalexml.py index ee53381b22..fba8d7fdd5 100755 --- a/util/locale_database/cldr2qlocalexml.py +++ b/util/locale_database/cldr2qlocalexml.py @@ -61,13 +61,13 @@ import enumdata import xpathlite from xpathlite import DraftResolution, findAlias, findEntry, findTagsInFile from dateconverter import convert_date -from qlocalexml import Locale +from qlocalexml import Locale, QLocaleXmlWriter # TODO: make calendars a command-line option calendars = ['gregorian', 'persian', 'islamic'] # 'hebrew' findEntryInFile = xpathlite._findEntryInFile -def wrappedwarn(prefix, tokens): - return sys.stderr.write( +def wrappedwarn(err, prefix, tokens): + return err.write( '\n'.join(textwrap.wrap(prefix + ', '.join(tokens), subsequent_indent=' ', width=80)) + '\n') @@ -101,6 +101,7 @@ def parse_number_format(patterns, data): result.append(pattern) return result +cldr_dir = None def raiseUnknownCode(code, form, cache={}): """Check whether an unknown code could be supported. @@ -193,8 +194,8 @@ def getNumberSystems(cache={}): """Cached look-up of number system information. Pass no arguments. Returns a mapping from number system names to, - for each system, a mapping with keys u'digits', u'type' and - u'id'\n""" + for each system, a mapping with keys 'digits', 'type' and 'id'. + Relies on global cldr_dir being set before it's first called.\n""" if not cache: for ns in findTagsInFile(os.path.join(cldr_dir, '..', 'supplemental', 'numberingSystems.xml'), @@ -419,26 +420,7 @@ def _generateLocaleInfo(path, language_code, script_code, country_code, variant_ return Locale(result) -def addEscapes(s): - result = '' - for c in s: - n = ord(c) - if n < 128: - result += c - else: - result += "\\x" - result += "%02x" % (n) - return result - -def unicodeStr(s): - utf8 = s.encode('utf-8') - return "" + str(len(utf8)) + "" + addEscapes(utf8) + "" - -def usage(): - print "Usage: cldr2qlocalexml.py " - sys.exit() - -def integrateWeekData(filePath): +def integrateWeekData(filePath, locale_database): if not filePath.endswith(".xml"): return {} @@ -510,111 +492,6 @@ def splitLocale(name): tag = (tag if tag else tags.next(),) sys.stderr.write('Ignoring unparsed cruft %s in %s\n' % ('_'.join(tag + tuple(tags)), name)) -if len(sys.argv) != 2: - usage() - -cldr_dir = sys.argv[1] - -if not os.path.isdir(cldr_dir): - usage() - -cldr_files = os.listdir(cldr_dir) - -locale_database = {} - -# see http://www.unicode.org/reports/tr35/tr35-info.html#Default_Content -defaultContent_locales = [] -for ns in findTagsInFile(os.path.join(cldr_dir, '..', 'supplemental', - 'supplementalMetadata.xml'), - 'metadata/defaultContent'): - for data in ns[1:][0]: - if data[0] == u"locales": - defaultContent_locales += data[1].split() - -skips = [] -for file in defaultContent_locales: - try: - language_code, script_code, country_code = splitLocale(file) - except ValueError: - sys.stderr.write('skipping defaultContent locale "' + file + '" [neither two nor three tags]\n') - continue - - if not (script_code or country_code): - sys.stderr.write('skipping defaultContent locale "' + file + '" [second tag is neither script nor territory]\n') - continue - - try: - l = _generateLocaleInfo(cldr_dir + "/" + file + ".xml", language_code, script_code, country_code) - if not l: - skips.append(file) - continue - except xpathlite.Error as e: - sys.stderr.write('skipping defaultContent locale "%s" (%s)\n' % (file, str(e))) - continue - - locale_database[(l.language_id, l.script_id, l.country_id, l.variant_code)] = l - -if skips: - wrappedwarn('skipping defaultContent locales [no locale info generated]: ', skips) - skips = [] - -for file in cldr_files: - try: - l = generateLocaleInfo(cldr_dir + "/" + file) - if not l: - skips.append(file) - continue - except xpathlite.Error as e: - sys.stderr.write('skipping file "%s" (%s)\n' % (file, str(e))) - continue - - locale_database[(l.language_id, l.script_id, l.country_id, l.variant_code)] = l - -if skips: - wrappedwarn('skipping files [no locale info generated]: ', skips) - -integrateWeekData(cldr_dir+"/../supplemental/supplementalData.xml") -locale_keys = locale_database.keys() -locale_keys.sort() - -cldr_version = 'unknown' -ldml = open(cldr_dir+"/../dtd/ldml.dtd", "r") -for line in ldml: - if 'version cldrVersion CDATA #FIXED' in line: - cldr_version = line.split('"')[1] - -print "" -print " " + cldr_version + "" -print " " -for id in enumdata.language_list: - l = enumdata.language_list[id] - print " " - print " " + l[0] + "" - print " " + str(id) + "" - print " " + l[1] + "" - print " " -print " " - -print " " -for id in enumdata.script_list: - l = enumdata.script_list[id] - print " " -print " " - -print " " -for id in enumdata.country_list: - l = enumdata.country_list[id] - print " " - print " " + l[0] + "" - print " " + str(id) + "" - print " " + l[1] + "" - print " " -print " " - def _parseLocale(l): language = "AnyLanguage" script = "AnyScript" @@ -651,48 +528,135 @@ def _parseLocale(l): return (language, script, country) -skips = [] -print " " -for ns in findTagsInFile(cldr_dir + "/../supplemental/likelySubtags.xml", "likelySubtags"): - tmp = {} - for data in ns[1:][0]: # ns looks like this: [u'likelySubtag', [(u'from', u'aa'), (u'to', u'aa_Latn_ET')]] - tmp[data[0]] = data[1] +def likelySubtags(root, err): + skips = [] + for ns in findTagsInFile(os.path.join(root, 'supplemental', 'likelySubtags.xml'), "likelySubtags"): + tmp = {} + for data in ns[1:][0]: # ns looks like this: [u'likelySubtag', [(u'from', u'aa'), (u'to', u'aa_Latn_ET')]] + tmp[data[0]] = data[1] + + try: + from_language, from_script, from_country = _parseLocale(tmp[u"from"]) + to_language, to_script, to_country = _parseLocale(tmp[u"to"]) + except xpathlite.Error as e: + if tmp[u'to'].startswith(tmp[u'from']) and str(e) == 'unknown language code "%s"' % tmp[u'from']: + skips.append(tmp[u'to']) + else: + sys.stderr.write('skipping likelySubtag "%s" -> "%s" (%s)\n' % (tmp[u"from"], tmp[u"to"], str(e))) + continue + # substitute according to http://www.unicode.org/reports/tr35/#Likely_Subtags + if to_country == "AnyCountry" and from_country != to_country: + to_country = from_country + if to_script == "AnyScript" and from_script != to_script: + to_script = from_script + + yield ((from_language, from_script, from_country), + (to_language, to_script, to_country)) + if skips: + wrappedwarn(err, 'skipping likelySubtags (for unknown language codes): ', skips) + +def usage(err, name, message = ''): + err.write("""Usage: {} [out-file.xml] +""".format(name)) # TODO: expand + if message: + err.write('\n' + message + '\n') + +def main(args, out, err): + name = args.pop(0) + + if len(args) < 1: + usage(err, name) + return 1 + + global cldr_dir + cldr_dir = args.pop(0) + if not os.path.isdir(cldr_dir): + usage(err, name, 'Where did you unpack the CLDR data files ?') + return 1 + + if len(args) > 1: + usage(err, name, 'Too many arguments passed') + return 1 + if args: + qxml = open(args.pop(0), 'w') + else: + qxml = out - try: - from_language, from_script, from_country = _parseLocale(tmp[u"from"]) - to_language, to_script, to_country = _parseLocale(tmp[u"to"]) - except xpathlite.Error as e: - if tmp[u'to'].startswith(tmp[u'from']) and str(e) == 'unknown language code "%s"' % tmp[u'from']: - skips.append(tmp[u'to']) - else: - sys.stderr.write('skipping likelySubtag "%s" -> "%s" (%s)\n' % (tmp[u"from"], tmp[u"to"], str(e))) - continue - # substitute according to http://www.unicode.org/reports/tr35/#Likely_Subtags - if to_country == "AnyCountry" and from_country != to_country: - to_country = from_country - if to_script == "AnyScript" and from_script != to_script: - to_script = from_script - - print " " - print " " - print " " + from_language + "" - print " " - print " " + from_country + "" - print " " - print " " - print " " + to_language + "" - print " " - print " " + to_country + "" - print " " - print " " -print " " -if skips: - wrappedwarn('skipping likelySubtags (for unknown language codes): ', skips) -print " " - -Locale.C(calendars).toXml(calendars) -for key in locale_keys: - locale_database[key].toXml(calendars) - -print " " -print "" + getNumberSystems(cldr_dir) + cldr_files = os.listdir(cldr_dir) + locale_database = {} + + # see http://www.unicode.org/reports/tr35/tr35-info.html#Default_Content + defaultContent_locales = [] + for ns in findTagsInFile(os.path.join(cldr_dir, '..', 'supplemental', + 'supplementalMetadata.xml'), + 'metadata/defaultContent'): + for data in ns[1:][0]: + if data[0] == u"locales": + defaultContent_locales += data[1].split() + + skips = [] + for file in defaultContent_locales: + try: + language_code, script_code, country_code = splitLocale(file) + except ValueError: + sys.stderr.write('skipping defaultContent locale "' + file + '" [neither two nor three tags]\n') + continue + + if not (script_code or country_code): + sys.stderr.write('skipping defaultContent locale "' + file + '" [second tag is neither script nor territory]\n') + continue + + try: + l = _generateLocaleInfo(cldr_dir + "/" + file + ".xml", language_code, script_code, country_code) + if not l: + skips.append(file) + continue + except xpathlite.Error as e: + sys.stderr.write('skipping defaultContent locale "{}" ({})\n'.format(file, str(e))) + continue + + locale_database[(l.language_id, l.script_id, l.country_id, l.variant_code)] = l + + if skips: + wrappedwarn(err, 'skipping defaultContent locales [no locale info generated]: ', skips) + skips = [] + + for file in cldr_files: + try: + l = generateLocaleInfo(cldr_dir + "/" + file) + if not l: + skips.append(file) + continue + except xpathlite.Error as e: + sys.stderr.write('skipping file "{}" ({})\n'.format(file, str(e))) + continue + + locale_database[(l.language_id, l.script_id, l.country_id, l.variant_code)] = l + + if skips: + wrappedwarn(err, 'skipping files [no locale info generated]: ', skips) + + integrateWeekData(cldr_dir + "/../supplemental/supplementalData.xml", locale_database) + cldr_version = 'unknown' + with open(cldr_dir+"/../dtd/ldml.dtd", "r") as ldml: + for line in ldml: + if 'version cldrVersion CDATA #FIXED' in line: + cldr_version = line.split('"')[1] + + xmlOut = QLocaleXmlWriter(qxml.write) + xmlOut.version(cldr_version) + xmlOut.enumData(enumdata.language_list, + enumdata.script_list, + enumdata.country_list) + xmlOut.likelySubTags(likelySubtags(os.path.split(cldr_dir)[0], err)) + xmlOut.locales(locale_database, calendars) + xmlOut.close() + if qxml is not out: + qxml.close() + + return 0 + +if __name__ == '__main__': + import sys + sys.exit(main(sys.argv, sys.stdout, sys.stderr)) diff --git a/util/locale_database/qlocalexml.py b/util/locale_database/qlocalexml.py index 87e356b8a7..b64ff56c64 100644 --- a/util/locale_database/qlocalexml.py +++ b/util/locale_database/qlocalexml.py @@ -1,7 +1,7 @@ # coding=utf8 ############################################################################# ## -## Copyright (C) 2018 The Qt Company Ltd. +## Copyright (C) 2020 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the test suite of the Qt Toolkit. @@ -28,11 +28,17 @@ ############################################################################# """Shared serialization-scanning code for QLocaleXML format. -The Locale class is written by cldr2qlocalexml.py and read by qlocalexml2cpp.py +Provides classes: + Locale -- common data-type representing one locale as a namespace + QLocaleXmlWriter -- helper to write a QLocaleXML file + +Support: + Spacer -- provides control over indentation of the output. """ +from __future__ import print_function from xml.sax.saxutils import escape -import xpathlite +from xpathlite import Error # Tools used by Locale: def camel(seq): @@ -43,10 +49,14 @@ def camel(seq): def camelCase(words): return ''.join(camel(iter(words))) +def addEscapes(s): + return ''.join(c if n < 128 else '\\x{:02x}'.format(n) + for n, c in ((ord(c), c) for c in s)) + def ordStr(c): if len(c) == 1: return str(ord(c)) - raise xpathlite.Error('Unable to handle value "%s"' % addEscapes(c)) + raise Error('Unable to handle value "{}"'.format(addEscapes(c))) # Fix for a problem with QLocale returning a character instead of # strings for QLocale::exponential() and others. So we fallback to @@ -69,6 +79,8 @@ def convertFormat(format): * https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table * QDateTimeParser::parseFormat() and QLocalePrivate::dateTimeToString() """ + # Compare and contrast dateconverter.py's convert_date(). + # Need to (check consistency and) reduce redundancy ! result = "" i = 0 while i < len(format): @@ -113,7 +125,163 @@ def convertFormat(format): return result -class Locale: +class Spacer (object): + def __init__(self, indent = None, initial = ''): + """Prepare to manage indentation and line breaks. + + Arguments are both optional. + + First argument, indent, is either None (its default, for + 'minifying'), an ingeter (number of spaces) or the unit of + text that is to be used for each indentation level (e.g. '\t' + to use tabs). If indent is None, no indentation is added, nor + are line-breaks; otherwise, self(text), for non-empty text, + shall end with a newline and begin with indentation. + + Second argument, initial, is the initial indentation; it is + ignored if indent is None. Indentation increases after each + call to self(text) in which text starts with a tag and doesn't + include its end-tag; indentation decreases if text starts with + an end-tag. The text is not parsed any more carefully than + just described. + """ + if indent is None: + self.__call = lambda x: x + else: + self.__each = ' ' * indent if isinstance(indent, int) else indent + self.current = initial + self.__call = self.__wrap + + def __wrap(self, line): + if not line: + return '\n' + + indent = self.current + if line.startswith('') + tag = (line[1:] if cut < 0 else line[1 : cut]).strip().split()[0] + if ''.format(tag) not in line: + self.current += self.__each + return indent + line + '\n' + + def __call__(self, line): + return self.__call(line) + +class QLocaleXmlWriter (object): + def __init__(self, save = None, space = Spacer(4)): + """Set up to write digested CLDR data as QLocale XML. + + Arguments are both optional. + + First argument, save, is None (its default) or a callable that + will write content to where you intend to save it. If None, it + is replaced with a callable that prints the given content, + suppressing the newline (but see the following); this is + equivalent to passing sys.stdout.write. + + Second argument, space, is an object to call on each text + output to prepend indentation and append newlines, or not as + the case may be. The default is a Spacer(4), which grows + indent by four spaces after each unmatched new tag and shrinks + back on a close-tag (its parsing is naive, but adequate to how + this class uses it), while adding a newline to each line. + """ + self.__rawOutput = self.__printit if save is None else save + self.__wrap = space + self.__write('') + + # Output of various sections, in their usual order: + def enumData(self, languages, scripts, countries): + self.__enumTable('languageList', languages) + self.__enumTable('scriptList', scripts) + self.__enumTable('countryList', countries) + + def likelySubTags(self, entries): + self.__openTag('likelySubtags') + for have, give in entries: + self.__openTag('likelySubtag') + self.__likelySubTag('from', have) + self.__likelySubTag('to', give) + self.__closeTag('likelySubtag') + self.__closeTag('likelySubtags') + + def locales(self, locales, calendars): + self.__openTag('localeList') + self.__openTag('locale') + Locale.C(calendars).toXml(self.inTag, calendars) + self.__closeTag('locale') + keys = locales.keys() + keys.sort() + for key in keys: + self.__openTag('locale') + locales[key].toXml(self.inTag, calendars) + self.__closeTag('locale') + self.__closeTag('localeList') + + def version(self, cldrVersion): + self.inTag('version', cldrVersion) + + def inTag(self, tag, text): + self.__write('<{0}>{1}'.format(tag, text)) + + def close(self): + if self.__rawOutput != self.__complain: + self.__write('') + self.__rawOutput = self.__complain + + # Implementation details + @staticmethod + def __printit(text): + print(text, end='') + @staticmethod + def __complain(text): + raise Error('Attempted to write data after closing :-(') + + def __enumTable(self, tag, table): + self.__openTag(tag) + for key, value in table.iteritems(): + self.__openTag(tag[:-4]) + self.inTag('name', value[0]) + self.inTag('id', key) + self.inTag('code', value[1]) + self.__closeTag(tag[:-4]) + self.__closeTag(tag) + + def __likelySubTag(self, tag, likely): + self.__openTag(tag) + self.inTag('language', likely[0]) + self.inTag('script', likely[1]) + self.inTag('country', likely[2]) + # self.inTag('variant', likely[3]) + self.__closeTag(tag) + + def __openTag(self, tag): + self.__write('<{}>'.format(tag)) + def __closeTag(self, tag): + self.__write(''.format(tag)) + + def __write(self, line): + self.__rawOutput(self.__wrap(line)) + +class Locale (object): + """Holder for the assorted data representing one locale. + + Implemented as a namespace; its constructor and update() have the + same signatures as those of a dict, acting on the instance's + __dict__, so the results are accessed as attributes rather than + mapping keys.""" + def __init__(self, data=None, **kw): + self.update(data, **kw) + + def update(self, data=None, **kw): + if data: self.__dict__.update(data) + if kw: self.__dict__.update(kw) + + def __len__(self): # Used when testing as a boolean + return len(self.__dict__) + @staticmethod def propsMonthDay(scale, lengths=('long', 'short', 'narrow')): for L in lengths: @@ -176,19 +344,26 @@ class Locale: return cls(data) - def toXml(self, calendars=('gregorian',), indent=' ', tab=' '): - print indent + '' - inner = indent + tab + def toXml(self, write, calendars=('gregorian',)): + """Writes its data as QLocale XML. + + First argument, write, is a callable taking the name and + content of an XML element; it is expected to be the inTag + bound method of a QLocaleXmlWriter instance. + + Optional second argument is a list of calendar names, in the + form used by CLDR; its default is ('gregorian',). + """ get = lambda k: getattr(self, k) for key in ('language', 'script', 'country'): - print inner + "<%s>" % key + get(key) + "" % key - print inner + "<%scode>" % key + get(key + '_code') + "" % key + write(key, get(key)) + write('{}code'.format(key), get('{}_code'.format(key))) for key in ('decimal', 'group', 'zero'): - print inner + "<%s>" % key + ordStr(get(key)) + "" % key + write(key, ordStr(get(key))) for key, std in (('list', ';'), ('percent', '%'), ('minus', '-'), ('plus', '+'), ('exp', 'e')): - print inner + "<%s>" % key + fixOrdStr(get(key), std) + "" % key + write(key, fixOrdStr(get(key), std)) for key in ('languageEndonym', 'countryEndonym', 'quotationStart', 'quotationEnd', @@ -206,16 +381,10 @@ class Locale: '_'.join((k, cal)) for k in self.propsMonthDay('months') for cal in calendars): - print inner + "<%s>%s" % (key, escape(get(key)).encode('utf-8'), key) + write(key, escape(get(key)).encode('utf-8')) for key in ('currencyDigits', 'currencyRounding'): - print inner + "<%s>%d" % (key, get(key), key) - - print indent + "" - - def __init__(self, data=None, **kw): - if data: self.__dict__.update(data) - if kw: self.__dict__.update(kw) + write(key, get(key)) # Tools used by __monthNames: def fullName(i, name): return name @@ -261,8 +430,8 @@ class Locale: for cal in calendars: try: data = known[cal] - except KeyError: # Need to add an entry to known, above. - print 'Unsupported calendar:', cal + except KeyError as e: # Need to add an entry to known, above. + e.args += ('Unsupported calendar:', cal) raise names, get = data[0] + ('',), data[1:] for n, size in enumerate(sizes): @@ -279,7 +448,7 @@ class Locale: 'Thursday', 'Friday', 'Saturday', ''), quantifiers=('k', 'M', 'G', 'T', 'P', 'E')): """Returns an object representing the C locale.""" - return cls(dict(cls.__monthNames(calendars)), + return cls(cls.__monthNames(calendars), language='C', language_code='0', languageEndonym='', script='AnyScript', script_code='0', country='AnyCountry', country_code='0', countryEndonym='', -- cgit v1.2.3 From 4d9f1a87de7a6e50e89f96836bc2f0cf6e229dda Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Tue, 25 Feb 2020 12:30:06 +0100 Subject: Move qlocalexml2cpp.py's XML-reading to QLocaleXmlReader This new class mirrors the existing QLocaleXmlWriter and places the two side-by-side in qlocalexml.py, rather than having the writing and reading in separate places. Made judicious use of transformed versions of mappings to save repeated iteration of a mapping's entries to do lookups on fist entries of pair-values; several (id, name, code) data-sets are sometimes indexed by id, sometimes by name. Reworked the default_map, that the complicated compareLocaleKeys() used in sorting locale keys, to map IDs instead of names; the function also needed the locale_map so that it could convert IDs to names, which we can skip by going directly with IDs. Task-number: QTBUG-81344 Change-Id: Iff6a97f7f0755b56dda70d8a6796ec074c558910 Reviewed-by: Cristian Maureira-Fredes --- util/locale_database/qlocalexml.py | 155 +++++++++++++++ util/locale_database/qlocalexml2cpp.py | 345 +++++++-------------------------- 2 files changed, 223 insertions(+), 277 deletions(-) diff --git a/util/locale_database/qlocalexml.py b/util/locale_database/qlocalexml.py index b64ff56c64..8289bd785a 100644 --- a/util/locale_database/qlocalexml.py +++ b/util/locale_database/qlocalexml.py @@ -31,6 +31,7 @@ Provides classes: Locale -- common data-type representing one locale as a namespace QLocaleXmlWriter -- helper to write a QLocaleXML file + QLocaleXmlReader -- helper to read a QLocaleXML file back in Support: Spacer -- provides control over indentation of the output. @@ -125,6 +126,157 @@ def convertFormat(format): return result +class QLocaleXmlReader (object): + def __init__(self, filename): + self.root = self.__parse(filename) + # Lists of (id, name, code) triples: + languages = tuple(self.__loadMap('language')) + scripts = tuple(self.__loadMap('script')) + countries = tuple(self.__loadMap('country')) + self.__likely = tuple(self.__likelySubtagsMap()) + # Mappings {ID: (name, code)} + self.languages = dict((v[0], v[1:]) for v in languages) + self.scripts = dict((v[0], v[1:]) for v in scripts) + self.countries = dict((v[0], v[1:]) for v in countries) + # Private mappings {name: (ID, code)} + self.__langByName = dict((v[1], (v[0], v[2])) for v in languages) + self.__textByName = dict((v[1], (v[0], v[2])) for v in scripts) + self.__landByName = dict((v[1], (v[0], v[2])) for v in countries) + # Other properties: + self.dupes = set(v[1] for v in languages) & set(v[1] for v in countries) + self.cldrVersion = self.__firstChildText(self.root, "version") + + def loadLocaleMap(self, calendars, grumble = lambda text: None): + kid = self.__firstChildText + likely = dict(self.__likely) + for elt in self.__eachEltInGroup(self.root, 'localeList', 'locale'): + locale = Locale.fromXmlData(lambda k: kid(elt, k), calendars) + language = self.__langByName[locale.language][0] + script = self.__textByName[locale.script][0] + country = self.__landByName[locale.country][0] + + if language != 1: # C + if country == 0: + grumble('loadLocaleMap: No country id for "{}"\n'.format(locale.language)) + + if script == 0: + # Find default script for the given language and country - see: + # http://www.unicode.org/reports/tr35/#Likely_Subtags + try: + try: + to = likely[(locale.language, 'AnyScript', locale.country)] + except KeyError: + to = likely[(locale.language, 'AnyScript', 'AnyCountry')] + except KeyError: + pass + else: + locale.script = to[1] + script = self.__textByName[locale.script][0] + + yield (language, script, country), locale + + def languageIndices(self, locales): + index = 0 + for key, value in self.languages.iteritems(): + i, count = 0, locales.count(key) + if count > 0: + i = index + index += count + yield i, value[0] + + def likelyMap(self): + def tag(t): + lang, script, land = t + yield lang[1] if lang[0] else 'und' + if script[0]: yield script[1] + if land[0]: yield land[1] + + def ids(t): + return tuple(x[0] for x in t) + + for i, pair in enumerate(self.__likely, 1): + have = self.__fromNames(pair[0]) + give = self.__fromNames(pair[1]) + yield ('_'.join(tag(have)), ids(have), + '_'.join(tag(give)), ids(give), + i == len(self.__likely)) + + def defaultMap(self): + """Map language and script to their default country by ID. + + Yields ((language, script), country) wherever the likely + sub-tags mapping says language's default locale uses the given + script and country.""" + for have, give in self.__likely: + if have[1:] == ('AnyScript', 'AnyCountry') and give[2] != 'AnyCountry': + assert have[0] == give[0], (have, give) + yield ((self.__langByName[give[0]][0], + self.__textByName[give[1]][0]), + self.__landByName[give[2]][0]) + + # Implementation details: + def __loadMap(self, category): + kid = self.__firstChildText + for element in self.__eachEltInGroup(self.root, category + 'List', category): + yield int(kid(element, 'id')), kid(element, 'name'), kid(element, 'code') + + def __likelySubtagsMap(self): + def triplet(element, keys=('language', 'script', 'country'), kid = self.__firstChildText): + return tuple(kid(element, key) for key in keys) + + kid = self.__firstChildElt + for elt in self.__eachEltInGroup(self.root, 'likelySubtags', 'likelySubtag'): + yield triplet(kid(elt, "from")), triplet(kid(elt, "to")) + + def __fromNames(self, names): + return self.__langByName[names[0]], self.__textByName[names[1]], self.__landByName[names[2]] + + # DOM access: + from xml.dom import minidom + @staticmethod + def __parse(filename, read = minidom.parse): + return read(filename).documentElement + + @staticmethod + def __isNodeNamed(elt, name, TYPE=minidom.Node.ELEMENT_NODE): + return elt.nodeType == TYPE and elt.nodeName == name + del minidom + + @staticmethod + def __eltWords(elt): + child = elt.firstChild + while child: + if child.nodeType == elt.TEXT_NODE: + yield child.nodeValue + child = child.nextSibling + + @classmethod + def __firstChildElt(cls, parent, name): + child = parent.firstChild + while child: + if cls.__isNodeNamed(child, name): + return child + child = child.nextSibling + + raise Error('No {} child found'.format(name)) + + @classmethod + def __firstChildText(cls, elt, key): + return ' '.join(cls.__eltWords(cls.__firstChildElt(elt, key))) + + @classmethod + def __eachEltInGroup(cls, parent, group, key): + try: + element = cls.__firstChildElt(parent, group).firstChild + except Error: + element = None + + while element: + if cls.__isNodeNamed(element, key): + yield element + element = element.nextSibling + + class Spacer (object): def __init__(self, indent = None, initial = ''): """Prepare to manage indentation and line breaks. @@ -403,6 +555,9 @@ class Locale (object): @staticmethod def __monthNames(calendars, known={ # Map calendar to (names, extractors...): + # TODO: do we even need these ? CLDR's root.xml seems to + # have them, complete with yeartype="leap" handling for + # Hebrew's extra. 'gregorian': (('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), # Extractor pairs, (plain, standalone) diff --git a/util/locale_database/qlocalexml2cpp.py b/util/locale_database/qlocalexml2cpp.py index 7c00980bc4..eb76f02faa 100755 --- a/util/locale_database/qlocalexml2cpp.py +++ b/util/locale_database/qlocalexml2cpp.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 ############################################################################# ## -## Copyright (C) 2018 The Qt Company Ltd. +## Copyright (C) 2020 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the test suite of the Qt Toolkit. @@ -37,10 +37,9 @@ import os import sys import tempfile import datetime -import xml.dom.minidom from enumdata import language_aliases, country_aliases, script_aliases -from qlocalexml import Locale +from qlocalexml import QLocaleXmlReader # TODO: Make calendars a command-line parameter # map { CLDR name: Qt file name } @@ -73,55 +72,6 @@ def wrap_list(lst): yield head return ",\n".join(", ".join(x) for x in split(lst, 20)) -def isNodeNamed(elt, name, TYPE=xml.dom.minidom.Node.ELEMENT_NODE): - return elt.nodeType == TYPE and elt.nodeName == name - -def firstChildElt(parent, name): - child = parent.firstChild - while child: - if isNodeNamed(child, name): - return child - child = child.nextSibling - - raise Error('No %s child found' % name) - -def eachEltInGroup(parent, group, key): - try: - element = firstChildElt(parent, group).firstChild - except Error: - element = None - - while element: - if isNodeNamed(element, key): - yield element - element = element.nextSibling - -def eltWords(elt): - child = elt.firstChild - while child: - if child.nodeType == elt.TEXT_NODE: - yield child.nodeValue - child = child.nextSibling - -def firstChildText(elt, key): - return ' '.join(eltWords(firstChildElt(elt, key))) - -def loadMap(doc, category): - return dict((int(firstChildText(element, 'id')), - (firstChildText(element, 'name'), - firstChildText(element, 'code'))) - for element in eachEltInGroup(doc.documentElement, - category + 'List', category)) - -def loadLikelySubtagsMap(doc): - def triplet(element, keys=('language', 'script', 'country')): - return tuple(firstChildText(element, key) for key in keys) - - return dict((i, {'from': triplet(firstChildElt(elt, "from")), - 'to': triplet(firstChildElt(elt, "to"))}) - for i, elt in enumerate(eachEltInGroup(doc.documentElement, - 'likelySubtags', 'likelySubtag'))) - def fixedScriptName(name, dupes): # Don't .capitalize() as some names are already camel-case (see enumdata.py): name = ''.join(word[0].upper() + word[1:] for word in name.split()) @@ -142,106 +92,40 @@ def fixedLanguageName(name, dupes): return name.replace(" ", "") + "Language" return name.replace(" ", "") -def findDupes(country_map, language_map): - country_set = set(v[0] for a, v in country_map.iteritems()) - language_set = set(v[0] for a, v in language_map.iteritems()) - return country_set & language_set - -def languageNameToId(name, language_map): - for key in language_map.keys(): - if language_map[key][0] == name: - return key - return -1 - -def scriptNameToId(name, script_map): - for key in script_map.keys(): - if script_map[key][0] == name: - return key - return -1 - -def countryNameToId(name, country_map): - for key in country_map.keys(): - if country_map[key][0] == name: - return key - return -1 - -def loadLocaleMap(doc, language_map, script_map, country_map, likely_subtags_map): - result = {} - - for locale_elt in eachEltInGroup(doc.documentElement, "localeList", "locale"): - locale = Locale.fromXmlData(lambda k: firstChildText(locale_elt, k), calendars.keys()) - language_id = languageNameToId(locale.language, language_map) - if language_id == -1: - sys.stderr.write("Cannot find a language id for '%s'\n" % locale.language) - script_id = scriptNameToId(locale.script, script_map) - if script_id == -1: - sys.stderr.write("Cannot find a script id for '%s'\n" % locale.script) - country_id = countryNameToId(locale.country, country_map) - if country_id == -1: - sys.stderr.write("Cannot find a country id for '%s'\n" % locale.country) - - if language_id != 1: # C - if country_id == 0: - sys.stderr.write("loadLocaleMap: No country id for '%s'\n" % locale.language) - - if script_id == 0: - # find default script for a given language and country (see http://www.unicode.org/reports/tr35/#Likely_Subtags) - for key in likely_subtags_map.keys(): - tmp = likely_subtags_map[key] - if tmp["from"][0] == locale.language and tmp["from"][1] == "AnyScript" and tmp["from"][2] == locale.country: - locale.script = tmp["to"][1] - script_id = scriptNameToId(locale.script, script_map) - break - if script_id == 0 and country_id != 0: - # try with no country - for key in likely_subtags_map.keys(): - tmp = likely_subtags_map[key] - if tmp["from"][0] == locale.language and tmp["from"][1] == "AnyScript" and tmp["from"][2] == "AnyCountry": - locale.script = tmp["to"][1] - script_id = scriptNameToId(locale.script, script_map) - break - - result[(language_id, script_id, country_id)] = locale - - return result - def compareLocaleKeys(key1, key2): if key1 == key2: return 0 - if key1[0] == key2[0]: - l1 = compareLocaleKeys.locale_map[key1] - l2 = compareLocaleKeys.locale_map[key2] - - if (l1.language, l1.script) in compareLocaleKeys.default_map.keys(): - default = compareLocaleKeys.default_map[(l1.language, l1.script)] - if l1.country == default: - return -1 - if l2.country == default: - return 1 - - if key1[1] != key2[1]: - if (l2.language, l2.script) in compareLocaleKeys.default_map.keys(): - default = compareLocaleKeys.default_map[(l2.language, l2.script)] - if l2.country == default: - return 1 - if l1.country == default: - return -1 - - if key1[1] != key2[1]: - return key1[1] - key2[1] - else: + if key1[0] != key2[0]: # First sort by language: return key1[0] - key2[0] - return key1[2] - key2[2] + defaults = compareLocaleKeys.default_map + # maps {(language, script): country} by ID + try: + country = defaults[key1[:2]] + except KeyError: + pass + else: + if key1[2] == country: + return -1 + if key2[2] == country: + return 1 + if key1[1] == key2[1]: + return key1[2] - key2[2] + + try: + country = defaults[key2[:2]] + except KeyError: + pass + else: + if key2[2] == country: + return 1 + if key1[2] == country: + return -1 + + return key1[1] - key2[1] -def languageCount(language_id, locale_map): - result = 0 - for key in locale_map.keys(): - if key[0] == language_id: - result += 1 - return result def unicode2hex(s): lst = [] @@ -303,40 +187,6 @@ class StringData: fd.write(wrap_list(self.data)) fd.write("\n};\n") -def escapedString(s): - result = "" - i = 0 - while i < len(s): - if s[i] == '"': - result += '\\"' - i += 1 - else: - result += s[i] - i += 1 - s = result - - line = "" - need_escape = False - result = "" - for c in s: - if ord(c) < 128 and (not need_escape or ord(c.lower()) < ord('a') or ord(c.lower()) > ord('f')): - line += c - need_escape = False - else: - line += "\\x%02x" % (ord(c)) - need_escape = True - if len(line) > 80: - result = result + "\n" + '"' + line + '"' - line = "" - line += "\\0" - result = result + "\n" + '"' + line + '"' - if result[0] == "\n": - result = result[1:] - return result - -def printEscapedString(s): - print escapedString(s) - def currencyIsoCodeData(s): if s: return '{' + ",".join(str(ord(x)) for x in s) + '}' @@ -370,83 +220,25 @@ def main(): s = qlocaledata_file.readline() data_temp_file.write(GENERATED_BLOCK_START) - doc = xml.dom.minidom.parse(qlocalexml) - language_map = loadMap(doc, 'language') - script_map = loadMap(doc, 'script') - country_map = loadMap(doc, 'country') - likely_subtags_map = loadLikelySubtagsMap(doc) - default_map = {} - for key in likely_subtags_map.keys(): - tmp = likely_subtags_map[key] - if tmp["from"][1] == "AnyScript" and tmp["from"][2] == "AnyCountry" and tmp["to"][2] != "AnyCountry": - default_map[(tmp["to"][0], tmp["to"][1])] = tmp["to"][2] - locale_map = loadLocaleMap(doc, language_map, script_map, country_map, likely_subtags_map) - dupes = findDupes(language_map, country_map) - - cldr_version = firstChildText(doc.documentElement, "version") - data_temp_file.write(generated_template % (datetime.date.today(), cldr_version)) + reader = QLocaleXmlReader(qlocalexml) + locale_map = dict(reader.loadLocaleMap(calendars, sys.stderr.write)) + data_temp_file.write(generated_template % (datetime.date.today(), reader.cldrVersion)) # Likely subtags map data_temp_file.write("static const QLocaleId likely_subtags[] = {\n") - index = 0 - for key in likely_subtags_map.keys(): - tmp = likely_subtags_map[key] - from_language = languageNameToId(tmp["from"][0], language_map) - from_script = scriptNameToId(tmp["from"][1], script_map) - from_country = countryNameToId(tmp["from"][2], country_map) - to_language = languageNameToId(tmp["to"][0], language_map) - to_script = scriptNameToId(tmp["to"][1], script_map) - to_country = countryNameToId(tmp["to"][2], country_map) - - cmnt_from = "" - if from_language != 0: - cmnt_from = cmnt_from + language_map[from_language][1] - else: - cmnt_from = cmnt_from + "und" - if from_script != 0: - if cmnt_from: - cmnt_from = cmnt_from + "_" - cmnt_from = cmnt_from + script_map[from_script][1] - if from_country != 0: - if cmnt_from: - cmnt_from = cmnt_from + "_" - cmnt_from = cmnt_from + country_map[from_country][1] - cmnt_to = "" - if to_language != 0: - cmnt_to = cmnt_to + language_map[to_language][1] - else: - cmnt_to = cmnt_to + "und" - if to_script != 0: - if cmnt_to: - cmnt_to = cmnt_to + "_" - cmnt_to = cmnt_to + script_map[to_script][1] - if to_country != 0: - if cmnt_to: - cmnt_to = cmnt_to + "_" - cmnt_to = cmnt_to + country_map[to_country][1] - - data_temp_file.write(" ") - data_temp_file.write("{ %3d, %3d, %3d }, { %3d, %3d, %3d }" % (from_language, from_script, from_country, to_language, to_script, to_country)) - index += 1 - if index != len(likely_subtags_map): - data_temp_file.write(",") - else: - data_temp_file.write(" ") - data_temp_file.write(" // %s -> %s\n" % (cmnt_from, cmnt_to)) + for had, have, got, give, last in reader.likelyMap(): + data_temp_file.write(' {{ {:3d}, {:3d}, {:3d} }}'.format(*have)) + data_temp_file.write(', {{ {:3d}, {:3d}, {:3d} }}'.format(*give)) + data_temp_file.write(' ' if last else ',') + data_temp_file.write(' // {} -> {}\n'.format(had, got)) data_temp_file.write("};\n") data_temp_file.write("\n") # Locale index data_temp_file.write("static const quint16 locale_index[] = {\n") - index = 0 - for key in language_map.keys(): - i = 0 - count = languageCount(key, locale_map) - if count > 0: - i = index - index += count - data_temp_file.write("%6d, // %s\n" % (i, language_map[key][0])) + for index, name in reader.languageIndices(tuple(k[0] for k in locale_map)): + data_temp_file.write('{:6d}, // {}\n'.format(index, name)) data_temp_file.write(" 0 // trailing 0\n") data_temp_file.write("};\n\n") @@ -524,8 +316,7 @@ def main(): + '\n') locale_keys = locale_map.keys() - compareLocaleKeys.default_map = default_map - compareLocaleKeys.locale_map = locale_map + compareLocaleKeys.default_map = dict(reader.defaultMap()) locale_keys.sort(compareLocaleKeys) line_format = (' { ' @@ -616,10 +407,10 @@ def main(): # Language name list data_temp_file.write("static const char language_name_list[] =\n") data_temp_file.write('"Default\\0"\n') - for key in language_map.keys(): + for key, value in reader.languages.items(): if key == 0: continue - data_temp_file.write('"' + language_map[key][0] + '\\0"\n') + data_temp_file.write('"' + value[0] + '\\0"\n') data_temp_file.write(";\n") data_temp_file.write("\n") @@ -628,10 +419,10 @@ def main(): data_temp_file.write("static const quint16 language_name_index[] = {\n") data_temp_file.write(" 0, // AnyLanguage\n") index = 8 - for key in language_map.keys(): + for key, value in reader.languages.items(): if key == 0: continue - language = language_map[key][0] + language = value[0] data_temp_file.write("%6d, // %s\n" % (index, language)) index += len(language) + 1 data_temp_file.write("};\n") @@ -641,10 +432,10 @@ def main(): # Script name list data_temp_file.write("static const char script_name_list[] =\n") data_temp_file.write('"Default\\0"\n') - for key in script_map.keys(): + for key, value in reader.scripts.items(): if key == 0: continue - data_temp_file.write('"' + script_map[key][0] + '\\0"\n') + data_temp_file.write('"' + value[0] + '\\0"\n') data_temp_file.write(";\n") data_temp_file.write("\n") @@ -653,10 +444,10 @@ def main(): data_temp_file.write("static const quint16 script_name_index[] = {\n") data_temp_file.write(" 0, // AnyScript\n") index = 8 - for key in script_map.keys(): + for key, value in reader.scripts.items(): if key == 0: continue - script = script_map[key][0] + script = value[0] data_temp_file.write("%6d, // %s\n" % (index, script)) index += len(script) + 1 data_temp_file.write("};\n") @@ -666,10 +457,10 @@ def main(): # Country name list data_temp_file.write("static const char country_name_list[] =\n") data_temp_file.write('"Default\\0"\n') - for key in country_map.keys(): + for key, value in reader.countries.items(): if key == 0: continue - data_temp_file.write('"' + country_map[key][0] + '\\0"\n') + data_temp_file.write('"' + value[0] + '\\0"\n') data_temp_file.write(";\n") data_temp_file.write("\n") @@ -678,10 +469,10 @@ def main(): data_temp_file.write("static const quint16 country_name_index[] = {\n") data_temp_file.write(" 0, // AnyCountry\n") index = 8 - for key in country_map.keys(): + for key, value in reader.countries.items(): if key == 0: continue - country = country_map[key][0] + country = value[0] data_temp_file.write("%6d, // %s\n" % (index, country)) index += len(country) + 1 data_temp_file.write("};\n") @@ -690,31 +481,31 @@ def main(): # Language code list data_temp_file.write("static const unsigned char language_code_list[] =\n") - for key in language_map.keys(): - code = language_map[key][1] + for key, value in reader.languages.items(): + code = value[1] if len(code) == 2: code += r"\0" - data_temp_file.write('"%2s" // %s\n' % (code, language_map[key][0])) + data_temp_file.write('"%2s" // %s\n' % (code, value[0])) data_temp_file.write(";\n") data_temp_file.write("\n") # Script code list data_temp_file.write("static const unsigned char script_code_list[] =\n") - for key in script_map.keys(): - code = script_map[key][1] + for key, value in reader.scripts.items(): + code = value[1] for i in range(4 - len(code)): code += "\\0" - data_temp_file.write('"%2s" // %s\n' % (code, script_map[key][0])) + data_temp_file.write('"%2s" // %s\n' % (code, value[0])) data_temp_file.write(";\n") # Country code list data_temp_file.write("static const unsigned char country_code_list[] =\n") - for key in country_map.keys(): - code = country_map[key][1] + for key, value in reader.countries.items(): + code = value[1] if len(code) == 2: code += "\\0" - data_temp_file.write('"%2s" // %s\n' % (code, country_map[key][0])) + data_temp_file.write('"%2s" // %s\n' % (code, value[0])) data_temp_file.write(";\n") data_temp_file.write("\n") @@ -748,7 +539,7 @@ def main(): calendar_temp_file.write(s) s = calendar_template_file.readline() calendar_temp_file.write(GENERATED_BLOCK_START) - calendar_temp_file.write(generated_template % (datetime.date.today(), cldr_version)) + calendar_temp_file.write(generated_template % (datetime.date.today(), reader.cldrVersion)) calendar_temp_file.write("static const QCalendarLocale locale_data[] = {\n") calendar_temp_file.write(' // ' # IDs, width 7 (6 + comma) @@ -805,8 +596,8 @@ def main(): # Language enum qlocaleh_temp_file.write(" enum Language {\n") language = None - for key, value in language_map.items(): - language = fixedLanguageName(value[0], dupes) + for key, value in reader.languages.items(): + language = fixedLanguageName(value[0], reader.dupes) qlocaleh_temp_file.write(" " + language + " = " + str(key) + ",\n") qlocaleh_temp_file.write("\n " + @@ -822,8 +613,8 @@ def main(): # Script enum qlocaleh_temp_file.write(" enum Script {\n") script = None - for key, value in script_map.items(): - script = fixedScriptName(value[0], dupes) + for key, value in reader.scripts.items(): + script = fixedScriptName(value[0], reader.dupes) qlocaleh_temp_file.write(" " + script + " = " + str(key) + ",\n") qlocaleh_temp_file.write("\n " + ",\n ".join('%s = %s' % pair @@ -836,8 +627,8 @@ def main(): # Country enum qlocaleh_temp_file.write(" enum Country {\n") country = None - for key, value in country_map.items(): - country = fixedCountryName(value[0], dupes) + for key, value in reader.countries.items(): + country = fixedCountryName(value[0], reader.dupes) qlocaleh_temp_file.write(" " + country + " = " + str(key) + ",\n") qlocaleh_temp_file.write("\n " + ",\n ".join('%s = %s' % pair @@ -872,7 +663,7 @@ def main(): DOCSTRING = " QLocale's data is based on Common Locale Data Repository " while s: if DOCSTRING in s: - qlocaleqdoc_temp_file.write(DOCSTRING + "v" + cldr_version + ".\n") + qlocaleqdoc_temp_file.write(DOCSTRING + "v" + reader.cldrVersion + ".\n") else: qlocaleqdoc_temp_file.write(s) s = qlocaleqdoc_file.readline() -- cgit v1.2.3 From c3dea1ffca7e46319daed5b44895c6e09f51f3ea Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 19 Feb 2020 17:18:28 +0100 Subject: Move some shared code to a localetools module The time-zone script was importing two functions from the locale data generation script. Move them to a separate module, to which I'll shortly add some more shared utilities. Cleaned up some imports in the process. Combined qlocalexml2cpp's and xpathlit's error classes into a new Error class in the new module and made it a bit more like a proper python error class. Task-number: QTBUG-81344 Change-Id: Idbe0139ba9aaa2f823b8f7216dee1d2539c18b75 Reviewed-by: Cristian Maureira-Fredes --- util/locale_database/cldr2qlocalexml.py | 54 ++++++++++++++------------- util/locale_database/cldr2qtimezone.py | 20 ++++------ util/locale_database/localetools.py | 65 +++++++++++++++++++++++++++++++++ util/locale_database/qlocalexml.py | 2 +- util/locale_database/qlocalexml2cpp.py | 31 +--------------- util/locale_database/xpathlite.py | 8 +--- 6 files changed, 105 insertions(+), 75 deletions(-) create mode 100644 util/locale_database/localetools.py diff --git a/util/locale_database/cldr2qlocalexml.py b/util/locale_database/cldr2qlocalexml.py index fba8d7fdd5..41795ff634 100755 --- a/util/locale_database/cldr2qlocalexml.py +++ b/util/locale_database/cldr2qlocalexml.py @@ -58,14 +58,14 @@ import re import textwrap import enumdata -import xpathlite -from xpathlite import DraftResolution, findAlias, findEntry, findTagsInFile +from localetools import Error +from xpathlite import DraftResolution, findAlias, findEntry, findTagsInFile, codeMapsFromFile, \ + _findEntryInFile as findEntryInFile from dateconverter import convert_date from qlocalexml import Locale, QLocaleXmlWriter # TODO: make calendars a command-line option calendars = ['gregorian', 'persian', 'islamic'] # 'hebrew' -findEntryInFile = xpathlite._findEntryInFile def wrappedwarn(err, prefix, tokens): return err.write( '\n'.join(textwrap.wrap(prefix + ', '.join(tokens), @@ -116,19 +116,19 @@ def raiseUnknownCode(code, form, cache={}): type of code to look up. Do not pass further parameters (the next will deprive you of the cache). - Raises xpathlite.Error with a suitable message, that includes the - unknown code's full name if found. + Raises localetools.Error with a suitable message, that includes + the unknown code's full name if found. Relies on global cldr_dir being set before it's called; see tail of this file. """ if not cache: - cache.update(xpathlite.codeMapsFromFile(os.path.join(cldr_dir, 'en.xml'))) + cache.update(codeMapsFromFile(os.path.join(cldr_dir, 'en.xml'))) name = cache[form].get(code) msg = 'unknown %s code "%s"' % (form, code) if name: msg += ' - could use "%s"' % name - raise xpathlite.Error(msg) + raise Error(msg) def parse_list_pattern_part_format(pattern): # This is a very limited parsing of the format for list pattern part only. @@ -182,7 +182,7 @@ def generateLocaleInfo(path): # skip legacy/compatibility ones alias = findAlias(path) if alias: - raise xpathlite.Error('alias to "%s"' % alias) + raise Error('Alias to "{}"'.format(alias)) def code(tag): return findEntryInFile(path, 'identity/' + tag, attribute="type")[0] @@ -224,7 +224,7 @@ def _generateLocaleInfo(path, language_code, script_code, country_code, variant_ # ### actually there is only one locale with variant: en_US_POSIX # does anybody care about it at all? if variant_code: - raise xpathlite.Error('we do not support variants ("%s")' % variant_code) + raise Error('We do not support variants ("{}")'.format(variant_code)) language_id = enumdata.languageCodeToId(language_code) if language_id <= 0: @@ -283,23 +283,23 @@ def _generateLocaleInfo(path, language_code, script_code, country_code, variant_ numbering_system = None try: numbering_system = findEntry(path, "numbers/defaultNumberingSystem") - except xpathlite.Error: + except Error: pass def findEntryDef(path, xpath, value=''): try: return findEntry(path, xpath) - except xpathlite.Error: + except Error: return value def get_number_in_system(path, xpath, numbering_system): if numbering_system: try: return findEntry(path, xpath + "[numberSystem=" + numbering_system + "]") - except xpathlite.Error: + except Error: # in CLDR 1.9 number system was refactored for numbers (but not for currency) # so if previous findEntry doesn't work we should try this: try: return findEntry(path, xpath.replace("/symbols/", "/symbols[numberSystem=" + numbering_system + "]/")) - except xpathlite.Error: + except Error: # fallback to default pass return findEntry(path, xpath) @@ -368,7 +368,7 @@ def _generateLocaleInfo(path, language_code, script_code, country_code, variant_ for count in ('many', 'few', 'two', 'other', 'zero', 'one'): try: ans = findEntry(path, stem + 'unitPattern[count=%s]' % count) - except xpathlite.Error: + except Error: continue # TODO: epxloit count-handling, instead of discarding placeholders @@ -498,7 +498,7 @@ def _parseLocale(l): country = "AnyCountry" if l == "und": - raise xpathlite.Error("we are treating unknown locale like C") + raise Error('We treat unknown locale like C') parsed = splitLocale(l) language_code = parsed.next() @@ -511,19 +511,19 @@ def _parseLocale(l): if language_code != "und": language_id = enumdata.languageCodeToId(language_code) if language_id == -1: - raise xpathlite.Error('unknown language code "%s"' % language_code) + raise Error('Unknown language code "{}"'.format(language_code)) language = enumdata.language_list[language_id][0] if script_code: script_id = enumdata.scriptCodeToId(script_code) if script_id == -1: - raise xpathlite.Error('unknown script code "%s"' % script_code) + raise Error('Unknown script code "{}"'.format(script_code)) script = enumdata.script_list[script_id][0] if country_code: country_id = enumdata.countryCodeToId(country_code) if country_id == -1: - raise xpathlite.Error('unknown country code "%s"' % country_code) + raise Error('Unknown country code "{}"'.format(country_code)) country = enumdata.country_list[country_id][0] return (language, script, country) @@ -538,11 +538,13 @@ def likelySubtags(root, err): try: from_language, from_script, from_country = _parseLocale(tmp[u"from"]) to_language, to_script, to_country = _parseLocale(tmp[u"to"]) - except xpathlite.Error as e: - if tmp[u'to'].startswith(tmp[u'from']) and str(e) == 'unknown language code "%s"' % tmp[u'from']: - skips.append(tmp[u'to']) + except Error as e: + if (tmp['to'].startswith(tmp['from']) + and e.message == 'Unknown language code "{}"'.format(tmp['from'])): + skips.append(tmp['to']) else: - sys.stderr.write('skipping likelySubtag "%s" -> "%s" (%s)\n' % (tmp[u"from"], tmp[u"to"], str(e))) + sys.stderr.write('skipping likelySubtag "{}" -> "{}" ({})\n'.format( + tmp[u"from"], tmp[u"to"], e.message)) continue # substitute according to http://www.unicode.org/reports/tr35/#Likely_Subtags if to_country == "AnyCountry" and from_country != to_country: @@ -612,8 +614,8 @@ def main(args, out, err): if not l: skips.append(file) continue - except xpathlite.Error as e: - sys.stderr.write('skipping defaultContent locale "{}" ({})\n'.format(file, str(e))) + except Error as e: + sys.stderr.write('skipping defaultContent locale "{}" ({})\n'.format(file, e.message)) continue locale_database[(l.language_id, l.script_id, l.country_id, l.variant_code)] = l @@ -628,8 +630,8 @@ def main(args, out, err): if not l: skips.append(file) continue - except xpathlite.Error as e: - sys.stderr.write('skipping file "{}" ({})\n'.format(file, str(e))) + except Error as e: + sys.stderr.write('skipping file "{}" ({})\n'.format(file, e.message)) continue locale_database[(l.language_id, l.script_id, l.country_id, l.variant_code)] = l diff --git a/util/locale_database/cldr2qtimezone.py b/util/locale_database/cldr2qtimezone.py index 4c3609056d..7816abc9e1 100755 --- a/util/locale_database/cldr2qtimezone.py +++ b/util/locale_database/cldr2qtimezone.py @@ -54,20 +54,14 @@ The XML structure is as follows: import os import sys +import re import datetime import tempfile -import enumdata -import xpathlite -from xpathlite import DraftResolution -import re -import qlocalexml2cpp -findAlias = xpathlite.findAlias -findEntry = xpathlite.findEntry -findEntryInFile = xpathlite._findEntryInFile -findTagsInFile = xpathlite.findTagsInFile -unicode2hex = qlocalexml2cpp.unicode2hex -wrap_list = qlocalexml2cpp.wrap_list +import enumdata +from localetools import unicode2hex, wrap_list, Error +from xpathlite import DraftResolution, findAlias, findEntry, findTagsInFile, \ + _findEntryInFile as findEntryInFile class ByteArrayData: def __init__(self): @@ -343,13 +337,13 @@ if mapTimezones: else: data['countryId'] = enumdata.countryCodeToId(data['countryCode']) if data['countryId'] < 0: - raise xpathlite.Error("Unknown Country Code \"%s\"" % data['countryCode']) + raise Error('Unknown Country Code "{}"'.format(data['countryCode'])) data['country'] = enumdata.country_list[data['countryId']][0] windowsIdDict[data['windowsKey'], data['countryId']] = data if badZones: sys.stderr.write('\n\t'.join(["\nUnknown Windows ID, please add:"] + sorted(badZones)) + "\nto the windowIdList in cldr2qtimezone.py\n\n") - raise xpathlite.Error("Unknown Windows IDs") + raise Error('Unknown Windows IDs') print "Input file parsed, now writing data" diff --git a/util/locale_database/localetools.py b/util/locale_database/localetools.py new file mode 100644 index 0000000000..0d5c2acbbb --- /dev/null +++ b/util/locale_database/localetools.py @@ -0,0 +1,65 @@ +############################################################################# +## +## Copyright (C) 2020 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# +"""Utilities shared among the CLDR extraction tools. +Functions: + unicode2hex() -- converts unicode text to UCS-2 in hex form. + wrap_list() -- map list to comma-separated string, 20 entries per line. + +Classes: + Error -- A shared error class. +""" + +class Error (StandardError): + __upinit = StandardError.__init__ + def __init__(self, msg, *args): + self.__upinit(msg, *args) + self.message = msg + def __str__(self): + return self.message + +def unicode2hex(s): + lst = [] + for x in s: + v = ord(x) + if v > 0xFFFF: + # make a surrogate pair + # copied from qchar.h + high = (v >> 10) + 0xd7c0 + low = (v % 0x400 + 0xdc00) + lst.append(hex(high)) + lst.append(hex(low)) + else: + lst.append(hex(v)) + return lst + +def wrap_list(lst): + def split(lst, size): + while lst: + head, lst = lst[:size], lst[size:] + yield head + return ",\n".join(", ".join(x) for x in split(lst, 20)) diff --git a/util/locale_database/qlocalexml.py b/util/locale_database/qlocalexml.py index 8289bd785a..0b962157d2 100644 --- a/util/locale_database/qlocalexml.py +++ b/util/locale_database/qlocalexml.py @@ -39,7 +39,7 @@ Support: from __future__ import print_function from xml.sax.saxutils import escape -from xpathlite import Error +from localetools import Error # Tools used by Locale: def camel(seq): diff --git a/util/locale_database/qlocalexml2cpp.py b/util/locale_database/qlocalexml2cpp.py index eb76f02faa..c54c3953ae 100755 --- a/util/locale_database/qlocalexml2cpp.py +++ b/util/locale_database/qlocalexml2cpp.py @@ -37,9 +37,10 @@ import os import sys import tempfile import datetime -from enumdata import language_aliases, country_aliases, script_aliases from qlocalexml import QLocaleXmlReader +from enumdata import language_aliases, country_aliases, script_aliases +from localetools import unicode2hex, wrap_list, Error # TODO: Make calendars a command-line parameter # map { CLDR name: Qt file name } @@ -59,19 +60,6 @@ generated_template = """ """ -class Error: - def __init__(self, msg): - self.msg = msg - def __str__(self): - return self.msg - -def wrap_list(lst): - def split(lst, size): - while lst: - head, lst = lst[:size], lst[size:] - yield head - return ",\n".join(", ".join(x) for x in split(lst, 20)) - def fixedScriptName(name, dupes): # Don't .capitalize() as some names are already camel-case (see enumdata.py): name = ''.join(word[0].upper() + word[1:] for word in name.split()) @@ -127,21 +115,6 @@ def compareLocaleKeys(key1, key2): return key1[1] - key2[1] -def unicode2hex(s): - lst = [] - for x in s: - v = ord(x) - if v > 0xFFFF: - # make a surrogate pair - # copied from qchar.h - high = (v >> 10) + 0xd7c0 - low = (v % 0x400 + 0xdc00) - lst.append(hex(high)) - lst.append(hex(low)) - else: - lst.append(hex(v)) - return lst - class StringDataToken: def __init__(self, index, length): if index > 0xFFFF or length > 0xFFFF: diff --git a/util/locale_database/xpathlite.py b/util/locale_database/xpathlite.py index 97efaaab41..3da8b24656 100644 --- a/util/locale_database/xpathlite.py +++ b/util/locale_database/xpathlite.py @@ -31,6 +31,8 @@ import sys import os import xml.dom.minidom +from localetools import Error + class DraftResolution: # See http://www.unicode.org/cldr/process.html for description unconfirmed = 'unconfirmed' @@ -43,12 +45,6 @@ class DraftResolution: def toInt(self): return DraftResolution._values[self.resolution] -class Error: - def __init__(self, msg): - self.msg = msg - def __str__(self): - return self.msg - doc_cache = {} def parseDoc(file): if not doc_cache.has_key(file): -- cgit v1.2.3 From bb4242341b7d04a861e88828b06f784345e54a9b Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 19 Feb 2020 17:35:58 +0100 Subject: Add tools to localetools to facilitate source file recreation For now unused; later commits shall put them to use. Transcriber -- base, takes care of tempfile and renaming. SourceFileEditor -- handles copying parts before and after a common delimiter. Task-number: QTBUG-81344 Change-Id: I28cf977d0a08825fbb873fb330da6823b88ad3ed Reviewed-by: Cristian Maureira-Fredes --- util/locale_database/localetools.py | 99 +++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/util/locale_database/localetools.py b/util/locale_database/localetools.py index 0d5c2acbbb..29153366b3 100644 --- a/util/locale_database/localetools.py +++ b/util/locale_database/localetools.py @@ -26,14 +26,20 @@ ## ############################################################################# """Utilities shared among the CLDR extraction tools. + Functions: unicode2hex() -- converts unicode text to UCS-2 in hex form. wrap_list() -- map list to comma-separated string, 20 entries per line. Classes: Error -- A shared error class. + Transcriber -- edit a file by writing a temporary file, then renaming. + SourceFileEditor -- adds standard prelude and tail handling to Transcriber. """ +import os +import tempfile + class Error (StandardError): __upinit = StandardError.__init__ def __init__(self, msg, *args): @@ -63,3 +69,96 @@ def wrap_list(lst): head, lst = lst[:size], lst[size:] yield head return ",\n".join(", ".join(x) for x in split(lst, 20)) + +class Transcriber (object): + """Helper class to facilitate rewriting source files. + + This class takes care of the temporary file manipulation. Derived + classes need to implement transcribing of the content, with + whatever modifications they may want. Members reader and writer + are exposed; use writer.write() to output to the new file; use + reader.readline() or iterate reader to read the original. + + Callers should call close() on success or cleanup() on failure (to + clear away the temporary file). + """ + def __init__(self, path, temp): + # Open the old file + self.reader = open(path) + # Create a temp file to write the new data into + temp, tempPath = tempfile.mkstemp(os.path.split(path)[1], dir = temp) + self.__names = path, tempPath + self.writer = os.fdopen(temp, "w") + + def close(self): + self.reader.close() + self.writer.close() + self.reader = self.writer = None + source, temp = self.__names + os.remove(source) + os.rename(temp, source) + + def cleanup(self): + if self.__names: + self.reader.close() + self.writer.close() + # Remove temp-file: + os.remove(self.__names[1]) + self.__names = () + +class SourceFileEditor (Transcriber): + """Transcriber with transcription of code around a gnerated block. + + We have a common pattern of source files with a generated part + embedded in a context that's not touched by the regeneration + scripts. The generated part is, in each case, marked with a common + pair of start and end markers. We transcribe the old file to a new + temporary file; on success, we then remove the original and move + the new version to replace it. + + This class takes care of transcribing the parts before and after + the generated content; on creation, an instance will copy the + preamble up to the start marker; its close() will skip over the + original's generated content and resume transcribing with the end + marker. Derived classes need only implement the generation of the + content in between. + + Callers should call close() on success or cleanup() on failure (to + clear away the temporary file); see Transcriber. + """ + __upinit = Transcriber.__init__ + def __init__(self, path, temp): + """Set up the source file editor. + + Requires two arguments: the path to the source file to be read + and, on success, replaced with a new version; and the + directory in which to store the temporary file during the + rewrite.""" + self.__upinit(path, temp) + self.__copyPrelude() + + __upclose = Transcriber.close + def close(self): + self.__copyTail() + self.__upclose() + + # Implementation details: + GENERATED_BLOCK_START = '// GENERATED PART STARTS HERE' + GENERATED_BLOCK_END = '// GENERATED PART ENDS HERE' + + def __copyPrelude(self): + # Copy over the first non-generated section to the new file + for line in self.reader: + self.writer.write(line) + if line.strip() == self.GENERATED_BLOCK_START: + break + + def __copyTail(self): + # Skip through the old generated data in the old file + for line in self.reader: + if line.strip() == self.GENERATED_BLOCK_END: + self.writer.write(line) + break + # Transcribe the remainder: + for line in self.reader: + self.writer.write(line) -- cgit v1.2.3 From 5b1c33cc7834b0811784980f4b1ab9d31863fbe8 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 19 Feb 2020 16:10:45 +0100 Subject: Rework cldr2qtimezone.py into more maintainable form Broke out the updating of a source file to a ZoneIdWriter helper class, which enables tidying away the temporary file if we fail. Collected up the rest of the script into a main() that's now called from a __name__ == '__main__' block. Rationalized the imports. Eliminated an inefficient lookup function by constructing a suitable dict() before entering the loop that needed it. Separated the "data you might need to update" tables from the code that does the work, to make it easier for those adding support for new zones to see what they're doing. Removed the spurious $Revision$ from the output and reworded the premable of the generated file. (It would seem CLDR no longer uses an RCS-based version-control system.) Generated output is otherwise unchanged. Task-number: QTBUG-81344 Change-Id: I7d9de8357ebcb599d154de9f862e25f7ade00390 Reviewed-by: Lars Knoll Reviewed-by: Cristian Maureira-Fredes --- src/corelib/time/qtimezoneprivate_data_p.h | 4 +- util/locale_database/cldr2qtimezone.py | 325 ++++++++++++++--------------- 2 files changed, 162 insertions(+), 167 deletions(-) diff --git a/src/corelib/time/qtimezoneprivate_data_p.h b/src/corelib/time/qtimezoneprivate_data_p.h index 822af9c703..6d2bbc83c1 100644 --- a/src/corelib/time/qtimezoneprivate_data_p.h +++ b/src/corelib/time/qtimezoneprivate_data_p.h @@ -115,8 +115,8 @@ struct QUtcData { // GENERATED PART STARTS HERE /* - This part of the file was generated on 2019-10-24 from the - Common Locale Data Repository v36 supplemental/windowsZones.xml file $Revision$ + This part of the file was generated on 2020-02-28 from the + Common Locale Data Repository v36 file supplemental/windowsZones.xml http://www.unicode.org/cldr/ diff --git a/util/locale_database/cldr2qtimezone.py b/util/locale_database/cldr2qtimezone.py index 7816abc9e1..f2d2003d53 100755 --- a/util/locale_database/cldr2qtimezone.py +++ b/util/locale_database/cldr2qtimezone.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 ############################################################################# ## -## Copyright (C) 2019 The Qt Company Ltd. +## Copyright (C) 2020 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the test suite of the Qt Toolkit. @@ -35,7 +35,7 @@ script and the qtbase root directory as second parameter. It shall update qtbase's src/corelib/time/qtimezoneprivate_data_p.h ready for use. -The XML structure is as follows: +The XML structure we read has the form: @@ -53,34 +53,18 @@ The XML structure is as follows: """ import os -import sys import re import datetime -import tempfile import enumdata -from localetools import unicode2hex, wrap_list, Error +from localetools import unicode2hex, wrap_list, Error, SourceFileEditor from xpathlite import DraftResolution, findAlias, findEntry, findTagsInFile, \ _findEntryInFile as findEntryInFile -class ByteArrayData: - def __init__(self): - self.data = [] - self.hash = {} - def append(self, s): - s = s + '\0' - if s in self.hash: - return self.hash[s] +### Data that may need updates in response to new entries in the CLDR file ### - lst = unicode2hex(s) - index = len(self.data) - if index > 65535: - print "\n\n\n#error Data index is too big!" - sys.stderr.write ("\n\n\nERROR: index exceeds the uint16 range! index = %d\n" % index) - sys.exit(1) - self.hash[s] = index - self.data += lst - return index +# This script shall report the update you need, if this arises. +# However, you may need to research the relevant zone's standard offset. # List of currently known Windows IDs. # If this script reports missing IDs, please add them here. @@ -227,12 +211,6 @@ windowsIdList = ( (u'Yakutsk Standard Time', 32400), ) -def windowsIdToKey(windowsId): - for index, pair in enumerate(windowsIdList): - if pair[0] == windowsId: - return index + 1 - return 0 - # List of standard UTC IDs to use. Not public so may be safely changed. # Do not remove IDs, as each entry is part of the API/behavior guarantee. # ( UTC Id, Offset Seconds ) @@ -279,42 +257,143 @@ utcIdList = ( (u'UTC+14:00', 50400), ) -def usage(): - print "Usage: cldr2qtimezone.py " - sys.exit() +### End of data that may need updates in response to CLDR ### -if len(sys.argv) != 3: - usage() - -cldrPath = sys.argv[1] -qtPath = sys.argv[2] +class ByteArrayData: + def __init__(self): + self.data = [] + self.hash = {} -if not os.path.isdir(cldrPath) or not os.path.isdir(qtPath): - usage() + def append(self, s): + s = s + '\0' + if s in self.hash: + return self.hash[s] -windowsZonesPath = cldrPath + "/supplemental/windowsZones.xml" -tempFileDir = qtPath -dataFilePath = qtPath + "/src/corelib/time/qtimezoneprivate_data_p.h" + lst = unicode2hex(s) + index = len(self.data) + if index > 0xffff: + raise Error('Index ({}) outside the uint16 range !'.format(index)) + self.hash[s] = index + self.data += lst + return index -if not (os.path.isfile(windowsZonesPath) and os.path.isfile(dataFilePath)): - usage() + def write(self, out, name): + out('\nstatic const char {}[] = {{\n'.format(name)) + out(wrap_list(self.data)) + out('\n};\n') -cldr_version = 'unknown' -ldml = open(cldrPath + "/dtd/ldml.dtd", "r") -for line in ldml: - if 'version cldrVersion CDATA #FIXED' in line: - cldr_version = line.split('"')[1] +class ZoneIdWriter (SourceFileEditor): + def write(self, version, defaults, windowsIds): + self.__writeWarning(version) + windows, iana = self.__writeTables(self.writer.write, defaults, windowsIds) + windows.write(self.writer.write, 'windowsIdData') + iana.write(self.writer.write, 'ianaIdData') -# [[u'version', [(u'number', u'$Revision: 7825 $')]]] -versionNumber = findTagsInFile(windowsZonesPath, "version")[0][1][0][1] + def __writeWarning(self, version): + self.writer.write(""" +/* + This part of the file was generated on {} from the + Common Locale Data Repository v{} file supplemental/windowsZones.xml -mapTimezones = findTagsInFile(windowsZonesPath, "windowsZones/mapTimezones") + http://www.unicode.org/cldr/ -defaultDict = {} -windowsIdDict = {} + Do not edit this code: run cldr2qtimezone.py on updated (or + edited) CLDR data; see qtbase/util/locale_database/. +*/ -if mapTimezones: +""".format(str(datetime.date.today()), version)) + + @staticmethod + def __writeTables(out, defaults, windowsIds): + windowsIdData, ianaIdData = ByteArrayData(), ByteArrayData() + + # Write Windows/IANA table + out('// Windows ID Key, Country Enum, IANA ID Index\n') + out('static const QZoneData zoneDataTable[] = {\n') + for index, data in sorted(windowsIds.items()): + out(' {{ {:6d},{:6d},{:6d} }}, // {} / {}\n'.format( + data['windowsKey'], data['countryId'], + ianaIdData.append(data['ianaList']), + data['windowsId'], data['country'])) + out(' { 0, 0, 0 } // Trailing zeroes\n') + out('};\n\n') + + # Write Windows ID key table + out('// Windows ID Key, Windows ID Index, IANA ID Index, UTC Offset\n') + out('static const QWindowsData windowsDataTable[] = {\n') + for index, pair in enumerate(windowsIdList, 1): + out(' {{ {:6d},{:6d},{:6d},{:6d} }}, // {}\n'.format( + index, + windowsIdData.append(pair[0]), + ianaIdData.append(defaults[index]), + pair[1], pair[0])) + out(' { 0, 0, 0, 0 } // Trailing zeroes\n') + out('};\n\n') + + # Write UTC ID key table + out('// IANA ID Index, UTC Offset\n') + out('static const QUtcData utcDataTable[] = {\n') + for pair in utcIdList: + out(' {{ {:6d},{:6d} }}, // {}\n'.format( + ianaIdData.append(pair[0]), pair[1], pair[0])) + out(' { 0, 0 } // Trailing zeroes\n') + out('};\n') + + return windowsIdData, ianaIdData + +def usage(err, name, message=''): + err.write("""Usage: {} path/to/cldr/core/common path/to/qtbase +""".format(name)) # TODO: more interesting message + if message: + err.write('\n' + message + '\n') + +def main(args, out, err): + """Parses CLDR's data and updates Qt's representation of it. + + Takes sys.argv, sys.stdout, sys.stderr (or equivalents) as + arguments. Expects two command-line options: the common/ + subdirectory of the unpacked CLDR data-file tree and the root of + the qtbase module's checkout. Updates QTimeZone's private data + about Windows time-zone IDs.""" + name = args.pop(0) + if len(args) != 2: + usage(err, name, "Expected two arguments") + return 1 + + cldrPath = args.pop(0) + qtPath = args.pop(0) + + if not os.path.isdir(qtPath): + usage(err, name, "No such Qt directory: " + qtPath) + return 1 + if not os.path.isdir(cldrPath): + usage(err, name, "No such CLDR directory: " + cldrPath) + return 1 + + dataFilePath = os.path.join(qtPath, 'src', 'corelib', 'time', 'qtimezoneprivate_data_p.h') + if not os.path.isfile(dataFilePath): + usage(err, name, 'No such file: ' + dataFilePath) + return 1 + + windowsZonesPath = cldrPath + "/supplemental/windowsZones.xml" + if not os.path.isfile(windowsZonesPath): + usage(err, name, 'Failed to find CLDR data file: ' + windowsZonesPath) + return 1 + + cldrVersion = 'unknown' + ldml = open(cldrPath + "/dtd/ldml.dtd", "r") + for line in ldml: + if 'version cldrVersion CDATA #FIXED' in line: + cldrVersion = line.split('"')[1] + + mapTimezones = findTagsInFile(windowsZonesPath, "windowsZones/mapTimezones") + if not mapTimezones: + err.write('Failed to find time-zone data - aborting !\n') + return 1 + + defaultDict, windowsIdDict = {}, {} badZones = set() + winIdToIndex = dict((name, ind + 1) for ind, name in enumerate(x[0] for x in windowsIdList)) for mapZone in mapTimezones: # [u'mapZone', [(u'territory', u'MH'), (u'other', u'UTC+12'), (u'type', u'Pacific/Majuro Pacific/Kwajalein')]] if mapZone[0] == u'mapZone': @@ -327,8 +406,9 @@ if mapTimezones: if attribute[0] == u'type': data['ianaList'] = attribute[1] - data['windowsKey'] = windowsIdToKey(data['windowsId']) - if data['windowsKey'] <= 0: + try: + data['windowsKey'] = winIdToIndex[data['windowsId']] + except KeyError: badZones.add(data['windowsId']) countryId = 0 @@ -341,113 +421,28 @@ if mapTimezones: data['country'] = enumdata.country_list[data['countryId']][0] windowsIdDict[data['windowsKey'], data['countryId']] = data if badZones: - sys.stderr.write('\n\t'.join(["\nUnknown Windows ID, please add:"] + sorted(badZones)) - + "\nto the windowIdList in cldr2qtimezone.py\n\n") - raise Error('Unknown Windows IDs') - -print "Input file parsed, now writing data" - -GENERATED_BLOCK_START = "// GENERATED PART STARTS HERE\n" -GENERATED_BLOCK_END = "// GENERATED PART ENDS HERE\n" - -# Create a temp file to write the new data into -(newTempFile, newTempFilePath) = tempfile.mkstemp("qtimezone_data_p", dir=tempFileDir) -newTempFile = os.fdopen(newTempFile, "w") - -# Open the old file and copy over the first non-generated section to the new file -oldDataFile = open(dataFilePath, "r") -s = oldDataFile.readline() -while s and s != GENERATED_BLOCK_START: - newTempFile.write(s) - s = oldDataFile.readline() - -# Write out generated block start tag and warning -newTempFile.write(GENERATED_BLOCK_START) -newTempFile.write(""" -/* - This part of the file was generated on %s from the - Common Locale Data Repository v%s supplemental/windowsZones.xml file %s - - http://www.unicode.org/cldr/ - - Do not edit this code: run cldr2qtimezone.py on updated (or - edited) CLDR data; see qtbase/util/locale_database/. -*/ + err.write('\n\t'.join(["\nUnknown Windows ID, please add:"] + sorted(badZones)) + + "\nto the windowsIdList in cldr2qtimezone.py\n\n") + return 1 + + out.write('Input file parsed, now writing data\n') + try: + writer = ZoneIdWriter(dataFilePath, qtPath) + except IOError as e: + err.write('Failed to open files to transcribe: {}'.format(e.message or e.args[1])) + return 1 + + try: + writer.write(cldrVersion, defaultDict, windowsIdDict) + except Error as e: + writer.cleanup() + err.write('\nError in Windows ID data: ' + e.message + '\n') + return 1 + + writer.close() + out.write('Data generation completed, please check the new file at ' + dataFilePath + '\n') + return 0 -""" % (str(datetime.date.today()), cldr_version, versionNumber) ) - -windowsIdData = ByteArrayData() -ianaIdData = ByteArrayData() - -# Write Windows/IANA table -newTempFile.write("// Windows ID Key, Country Enum, IANA ID Index\n") -newTempFile.write("static const QZoneData zoneDataTable[] = {\n") -for index in sorted(windowsIdDict): - data = windowsIdDict[index] - newTempFile.write(" { %6d,%6d,%6d }, // %s / %s\n" - % (data['windowsKey'], - data['countryId'], - ianaIdData.append(data['ianaList']), - data['windowsId'], - data['country'])) -newTempFile.write(" { 0, 0, 0 } // Trailing zeroes\n") -newTempFile.write("};\n\n") - -print "Done Zone Data" - -# Write Windows ID key table -newTempFile.write("// Windows ID Key, Windows ID Index, IANA ID Index, UTC Offset\n") -newTempFile.write("static const QWindowsData windowsDataTable[] = {\n") -for index, pair in enumerate(windowsIdList): - newTempFile.write(" { %6d,%6d,%6d,%6d }, // %s\n" - % (index + 1, windowsIdData.append(pair[0]), - ianaIdData.append(defaultDict[index + 1]), pair[1], pair[0])) -newTempFile.write(" { 0, 0, 0, 0 } // Trailing zeroes\n") -newTempFile.write("};\n\n") - -print "Done Windows Data Table" - -# Write UTC ID key table -newTempFile.write("// IANA ID Index, UTC Offset\n") -newTempFile.write("static const QUtcData utcDataTable[] = {\n") -for pair in utcIdList: - newTempFile.write(" { %6d,%6d }, // %s\n" - % (ianaIdData.append(pair[0]), pair[1], pair[0])) -newTempFile.write(" { 0, 0 } // Trailing zeroes\n") -newTempFile.write("};\n\n") - -print "Done UTC Data Table" - -# Write out Windows ID's data -newTempFile.write("static const char windowsIdData[] = {\n") -newTempFile.write(wrap_list(windowsIdData.data)) -newTempFile.write("\n};\n\n") - -# Write out IANA ID's data -newTempFile.write("static const char ianaIdData[] = {\n") -newTempFile.write(wrap_list(ianaIdData.data)) -newTempFile.write("\n};\n") - -print "Done ID Data Table" - -# Write out the end of generated block tag -newTempFile.write(GENERATED_BLOCK_END) -s = oldDataFile.readline() - -# Skip through the old generated data in the old file -while s and s != GENERATED_BLOCK_END: - s = oldDataFile.readline() - -# Now copy the rest of the original file into the new file -s = oldDataFile.readline() -while s: - newTempFile.write(s) - s = oldDataFile.readline() - -# Now close the old and new file, delete the old file and copy the new file in its place -newTempFile.close() -oldDataFile.close() -os.remove(dataFilePath) -os.rename(newTempFilePath, dataFilePath) - -print "Data generation completed, please check the new file at " + dataFilePath +if __name__ == '__main__': + import sys + sys.exit(main(sys.argv, sys.stdout, sys.stderr)) -- cgit v1.2.3 From 9fab53a51317a1692ceb0069f212339bb0dd8780 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 19 Feb 2020 18:22:25 +0100 Subject: Rework qlocalexml2cpp.py to use writers based on Transcriber This saves repetition of temporary-file manipulation code. In the process, ensure that we tidy away temporary files on failure. Moved a comment in qlocale.h to *outside* the re-written portion, to save having to rewrite it every time. Added blank lines to separate script data from country data in the generated output. Changed 0s in one comment to zeros, to match another comment. Isolated use of sys to the __main__ block. Isolated use of enumdata to the new LocaleHeaderWriter class. Modernised all the string-formatting I touched. Task-number: QTBUG-81344 Change-Id: I5768e45d9a8ea23facc303b3dd8af8b3ccbf7ff2 Reviewed-by: Cristian Maureira-Fredes --- src/corelib/text/qlocale.h | 3 +- src/corelib/text/qlocale_data_p.h | 5 +- util/locale_database/qlocalexml2cpp.py | 946 +++++++++++++++------------------ 3 files changed, 443 insertions(+), 511 deletions(-) diff --git a/src/corelib/text/qlocale.h b/src/corelib/text/qlocale.h index 0dc9e1bd00..c1c3bd396b 100644 --- a/src/corelib/text/qlocale.h +++ b/src/corelib/text/qlocale.h @@ -73,8 +73,8 @@ class Q_CORE_EXPORT QLocale friend class QTextStreamPrivate; public: -// GENERATED PART STARTS HERE // see qlocale_data_p.h for more info on generated data +// GENERATED PART STARTS HERE enum Language { AnyLanguage = 0, C = 1, @@ -614,6 +614,7 @@ public: LastScript = JamoScript }; + enum Country { AnyCountry = 0, Afghanistan = 1, diff --git a/src/corelib/text/qlocale_data_p.h b/src/corelib/text/qlocale_data_p.h index 5f29a74d37..07aae0deca 100644 --- a/src/corelib/text/qlocale_data_p.h +++ b/src/corelib/text/qlocale_data_p.h @@ -77,7 +77,7 @@ static const int ImperialMeasurementSystemsCount = // GENERATED PART STARTS HERE /* - This part of the file was generated on 2019-10-24 from the + This part of the file was generated on 2020-03-18 from the Common Locale Data Repository v36 http://www.unicode.org/cldr/ @@ -1870,7 +1870,7 @@ static const QLocaleData locale_data[] = { { 367, 7, 225, 46, 44, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 0,6 , 0,6 , 53,10 , 63,17 , 37,5 , 8,10 , 0,28 , 0,28 , 85,14 , 0,28 , 0,28 , 85,14 , 0,2 , 0,2 , 45,4 , 5,17 , 22,23 , {85,83,68}, 159,3 , 0,7 , 8,5 , 4,0 , 0,0 , 0,0 , 2, 1, 7, 6, 7 }, // Chickasaw/Latin/United States { 368, 7, 225, 46, 44, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 0,6 , 0,6 , 53,10 , 63,17 , 37,5 , 8,10 , 0,28 , 0,28 , 85,14 , 0,28 , 0,28 , 85,14 , 0,2 , 0,2 , 45,4 , 5,17 , 22,23 , {85,83,68}, 159,3 , 0,7 , 8,5 , 4,0 , 0,0 , 0,0 , 2, 1, 7, 6, 7 }, // Muscogee/Latin/United States { 369, 7, 172, 46, 44, 59, 37, 48, 45, 43, 101, 8220, 8221, 8216, 8217, 0,6 , 0,6 , 0,6 , 0,6 , 53,10 , 63,17 , 37,5 , 8,10 , 0,28 , 0,28 , 85,14 , 0,28 , 0,28 , 85,14 , 0,2 , 0,2 , 45,4 , 5,17 , 22,23 , {80,76,78}, 273,2 , 0,7 , 8,5 , 4,0 , 0,0 , 0,0 , 2, 1, 1, 6, 7 }, // Silesian/Latin/Poland - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, {0,0,0}, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0, 0, 0, 0, 0 } // trailing 0s + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, {0,0,0}, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0, 0, 0, 0, 0 } // trailing zeros }; static const ushort list_pattern_part_data[] = { @@ -6602,6 +6602,7 @@ static const unsigned char script_code_list[] = "Hanb" // Han with Bopomofo "Jamo" // Jamo ; + static const unsigned char country_code_list[] = "ZZ\0" // AnyCountry "AF\0" // Afghanistan diff --git a/util/locale_database/qlocalexml2cpp.py b/util/locale_database/qlocalexml2cpp.py index c54c3953ae..59161ed9d0 100755 --- a/util/locale_database/qlocalexml2cpp.py +++ b/util/locale_database/qlocalexml2cpp.py @@ -34,51 +34,11 @@ the root of the qtbase check-out as second parameter. """ import os -import sys -import tempfile import datetime from qlocalexml import QLocaleXmlReader -from enumdata import language_aliases, country_aliases, script_aliases -from localetools import unicode2hex, wrap_list, Error - -# TODO: Make calendars a command-line parameter -# map { CLDR name: Qt file name } -calendars = {'gregorian': 'roman', 'persian': 'jalali', 'islamic': 'hijri',} # 'hebrew': 'hebrew', - -generated_template = """ -/* - This part of the file was generated on %s from the - Common Locale Data Repository v%s - - http://www.unicode.org/cldr/ - - Do not edit this section: instead regenerate it using - cldr2qlocalexml.py and qlocalexml2cpp.py on updated (or - edited) CLDR data; see qtbase/util/locale_database/. -*/ - -""" - -def fixedScriptName(name, dupes): - # Don't .capitalize() as some names are already camel-case (see enumdata.py): - name = ''.join(word[0].upper() + word[1:] for word in name.split()) - if name[-6:] != "Script": - name = name + "Script" - if name in dupes: - sys.stderr.write("\n\n\nERROR: The script name '%s' is messy" % name) - sys.exit(1) - return name - -def fixedCountryName(name, dupes): - if name in dupes: - return name.replace(" ", "") + "Country" - return name.replace(" ", "") - -def fixedLanguageName(name, dupes): - if name in dupes: - return name.replace(" ", "") + "Language" - return name.replace(" ", "") +from xml.dom import minidom +from localetools import unicode2hex, wrap_list, Error, Transcriber, SourceFileEditor def compareLocaleKeys(key1, key2): if key1 == key2: @@ -136,21 +96,17 @@ class StringData: lst = unicode2hex(s) index = len(self.data) - if index > 65535: - print "\n\n\n#error Data index is too big!" - sys.stderr.write ("\n\n\nERROR: index exceeds the uint16 range! index = %d\n" % index) - sys.exit(1) + if index > 0xffff: + raise Error('Data index {} is too big for uint16!'.format(index)) size = len(lst) - if size >= 65535: - print "\n\n\n#error Data is too big!" - sys.stderr.write ("\n\n\nERROR: data size exceeds the uint16 range! size = %d\n" % size) - sys.exit(1) + if size >= 0xffff: + raise Error('Data is too big ({}) for uint16 size!'.format(size)) token = None try: token = StringDataToken(index, size) except Error as e: - sys.stderr.write("\n\n\nERROR: %s: on data '%s'" % (e, s)) - sys.exit(1) + e.message += '(on data "{}")'.format(s) + raise self.hash[s] = token self.data += lst return token @@ -165,486 +121,460 @@ def currencyIsoCodeData(s): return '{' + ",".join(str(ord(x)) for x in s) + '}' return "{0,0,0}" -def usage(): - print "Usage: qlocalexml2cpp.py " - sys.exit(1) +class LocaleSourceEditor (SourceFileEditor): + __upinit = SourceFileEditor.__init__ + def __init__(self, path, temp, version): + self.__upinit(path, temp) + self.writer.write(""" +/* + This part of the file was generated on {} from the + Common Locale Data Repository v{} -GENERATED_BLOCK_START = "// GENERATED PART STARTS HERE\n" -GENERATED_BLOCK_END = "// GENERATED PART ENDS HERE\n" + http://www.unicode.org/cldr/ + + Do not edit this section: instead regenerate it using + cldr2qlocalexml.py and qlocalexml2cpp.py on updated (or + edited) CLDR data; see qtbase/util/locale_database/. +*/ -def main(): - if len(sys.argv) != 3: - usage() +""".format(datetime.date.today(), version)) + +class LocaleDataWriter (LocaleSourceEditor): + def likelySubtags(self, likely): + self.writer.write('static const QLocaleId likely_subtags[] = {\n') + for had, have, got, give, last in likely: + self.writer.write(' {{ {:3d}, {:3d}, {:3d} }}'.format(*have)) + self.writer.write(', {{ {:3d}, {:3d}, {:3d} }}'.format(*give)) + self.writer.write(' ' if last else ',') + self.writer.write(' // {} -> {}\n'.format(had, got)) + self.writer.write('};\n\n') + + def localeIndex(self, indices): + self.writer.write('static const quint16 locale_index[] = {\n') + for pair in indices: + self.writer.write('{:6d}, // {}\n'.format(*pair)) + self.writer.write(' 0 // trailing 0\n') + self.writer.write('};\n\n') + + def localeData(self, locales, names): + list_pattern_part_data = StringData('list_pattern_part_data') + date_format_data = StringData('date_format_data') + time_format_data = StringData('time_format_data') + days_data = StringData('days_data') + am_data = StringData('am_data') + pm_data = StringData('pm_data') + byte_unit_data = StringData('byte_unit_data') + currency_symbol_data = StringData('currency_symbol_data') + currency_display_name_data = StringData('currency_display_name_data') + currency_format_data = StringData('currency_format_data') + endonyms_data = StringData('endonyms_data') + + # Locale data + self.writer.write('static const QLocaleData locale_data[] = {\n') + # Table headings: keep each label centred in its field, matching line_format: + self.writer.write(' // ' + # Width 6 + comma + ' lang ' # IDs + 'script ' + ' terr ' + ' dec ' # Numeric punctuation + ' group ' + ' list ' # Delimiter for *numeric* lists + ' prcnt ' # Arithmetic symbols + ' zero ' + ' minus ' + ' plus ' + ' exp ' + # Width 8 + comma - to make space for these wide labels ! + ' quotOpn ' # Quotation marks + ' quotEnd ' + 'altQtOpn ' + 'altQtEnd ' + # Width 11 + comma + ' lpStart ' # List pattern + ' lpMid ' + ' lpEnd ' + ' lpTwo ' + ' sDtFmt ' # Date format + ' lDtFmt ' + ' sTmFmt ' # Time format + ' lTmFmt ' + ' ssDays ' # Days + ' slDays ' + ' snDays ' + ' sDays ' + ' lDays ' + ' nDays ' + ' am ' # am/pm indicators + ' pm ' + # Width 8 + comma + ' byte ' + ' siQuant ' + 'iecQuant ' + # Width 8+4 + comma + ' currISO ' + # Width 11 + comma + ' currSym ' # Currency formatting + ' currDsply ' + ' currFmt ' + ' currFmtNeg ' + ' endoLang ' # Name of language in itself, and of country + ' endoCntry ' + # Width 6 + comma + 'curDgt ' # Currency number representation + 'curRnd ' + 'dow1st ' # First day of week + ' wknd+ ' # Week-end start/end days + ' wknd-' + # No trailing space on last entry (be sure to + # pad before adding anything after it). + '\n') + + formatLine = ''.join(( + ' {{ ', + # Locale-identifier + '{:6d},' * 3, + # Numeric formats, list delimiter + '{:6d},' * 8, + # Quotation marks + '{:8d},' * 4, + # List patterns, date/time formats, month/day names, am/pm + '{:>11s},' * 16, + # SI/IEC byte-unit abbreviations + '{:>8s},' * 3, + # Currency ISO code + ' {:>10s}, ', + # Currency and endonyms + '{:>11s},' * 6, + # Currency formatting + '{:6d},{:6d}', + # Day of week and week-end + ',{:6d}' * 3, + ' }}')).format + for key in names: + locale = locales[key] + self.writer.write(formatLine( + key[0], key[1], key[2], + locale.decimal, + locale.group, + locale.listDelim, + locale.percent, + locale.zero, + locale.minus, + locale.plus, + locale.exp, + locale.quotationStart, + locale.quotationEnd, + locale.alternateQuotationStart, + locale.alternateQuotationEnd, + list_pattern_part_data.append(locale.listPatternPartStart), + list_pattern_part_data.append(locale.listPatternPartMiddle), + list_pattern_part_data.append(locale.listPatternPartEnd), + list_pattern_part_data.append(locale.listPatternPartTwo), + date_format_data.append(locale.shortDateFormat), + date_format_data.append(locale.longDateFormat), + time_format_data.append(locale.shortTimeFormat), + time_format_data.append(locale.longTimeFormat), + days_data.append(locale.standaloneShortDays), + days_data.append(locale.standaloneLongDays), + days_data.append(locale.standaloneNarrowDays), + days_data.append(locale.shortDays), + days_data.append(locale.longDays), + days_data.append(locale.narrowDays), + am_data.append(locale.am), + pm_data.append(locale.pm), + byte_unit_data.append(locale.byte_unit), + byte_unit_data.append(locale.byte_si_quantified), + byte_unit_data.append(locale.byte_iec_quantified), + currencyIsoCodeData(locale.currencyIsoCode), + currency_symbol_data.append(locale.currencySymbol), + currency_display_name_data.append(locale.currencyDisplayName), + currency_format_data.append(locale.currencyFormat), + currency_format_data.append(locale.currencyNegativeFormat), + endonyms_data.append(locale.languageEndonym), + endonyms_data.append(locale.countryEndonym), + locale.currencyDigits, + locale.currencyRounding, # unused (QTBUG-81343) + locale.firstDayOfWeek, + locale.weekendStart, + locale.weekendEnd) + + ', // {}/{}/{}\n'.format( + locale.language, locale.script, locale.country)) + self.writer.write(formatLine(*( # All zeros, matching the format: + (0,) * (3 + 8 + 4) + ('0,0',) * (16 + 3) + + (currencyIsoCodeData(0),) + + ('0,0',) * 6 + (0,) * (2 + 3) )) + + ' // trailing zeros\n') + self.writer.write('};\n') + + # StringData tables: + for data in (list_pattern_part_data, date_format_data, + time_format_data, days_data, + byte_unit_data, am_data, pm_data, currency_symbol_data, + currency_display_name_data, currency_format_data, + endonyms_data): + data.write(self.writer) + + @staticmethod + def __writeNameData(out, book, form): + out('static const char {}_name_list[] =\n'.format(form)) + out('"Default\\0"\n') + for key, value in book.items(): + if key == 0: + continue + out('"' + value[0] + '\\0"\n') + out(';\n\n') + + out('static const quint16 {}_name_index[] = {{\n'.format(form)) + out(' 0, // Any{}\n'.format(form.capitalize())) + index = 8 + for key, value in book.items(): + if key == 0: + continue + name = value[0] + out('{:6d}, // {}\n'.format(index, name)) + index += len(name) + 1 + out('};\n\n') + + @staticmethod + def __writeCodeList(out, book, form, width): + out('static const unsigned char {}_code_list[] =\n'.format(form)) + for key, value in book.items(): + code = value[1] + code += r'\0' * max(width - len(code), 0) + out('"{}" // {}\n'.format(code, value[0])) + out(';\n\n') + + def languageNames(self, languages): + self.__writeNameData(self.writer.write, languages, 'language') + + def scriptNames(self, scripts): + self.__writeNameData(self.writer.write, scripts, 'script') + + def countryNames(self, countries): + self.__writeNameData(self.writer.write, countries, 'country') + + # TODO: unify these next three into the previous three; kept + # separate for now to verify we're not changing data. + + def languageCodes(self, languages): + self.__writeCodeList(self.writer.write, languages, 'language', 3) + + def scriptCodes(self, scripts): + self.__writeCodeList(self.writer.write, scripts, 'script', 4) + + def countryCodes(self, countries): # TODO: unify with countryNames() + self.__writeCodeList(self.writer.write, countries, 'country', 3) + +class CalendarDataWriter (LocaleSourceEditor): + formatCalendar = ''.join(( + ' {{', + '{:6d}', + ',{:6d}' * 2, + ',{{{:>5s}}}' * 6, + '}}, ')).format + def write(self, calendar, locales, names): + months_data = StringData('months_data') - qlocalexml = sys.argv[1] - qtsrcdir = sys.argv[2] + self.writer.write('static const QCalendarLocale locale_data[] = {\n') + self.writer.write(' // ' + # IDs, width 7 (6 + comma) + + ' lang ' + + ' script' + + ' terr ' + # Month-name start-end pairs, width 8 (5 plus '{},'): + + ' sShort ' + + ' sLong ' + + ' sNarrow' + + ' short ' + + ' long ' + + ' narrow' + # No trailing space on last; be sure + # to pad before adding later entries. + + '\n') + for key in names: + locale = locales[key] + self.writer.write( + self.formatCalendar( + key[0], key[1], key[2], + months_data.append(locale.standaloneShortMonths[calendar]), + months_data.append(locale.standaloneLongMonths[calendar]), + months_data.append(locale.standaloneNarrowMonths[calendar]), + months_data.append(locale.shortMonths[calendar]), + months_data.append(locale.longMonths[calendar]), + months_data.append(locale.narrowMonths[calendar])) + + '// {}/{}/{}\n '.format(locale.language, locale.script, locale.country)) + self.writer.write(self.formatCalendar(*( (0,) * 3 + ('0,0',) * 6 )) + + '// trailing zeros\n') + self.writer.write('};\n') + months_data.write(self.writer) + +class LocaleHeaderWriter (SourceFileEditor): + __upinit = SourceFileEditor.__init__ + def __init__(self, path, temp, dupes): + self.__upinit(path, temp) + self.__dupes = dupes + + def languages(self, languages): + self.__enum('Language', languages, self.__language) + self.writer.write('\n') + + def countries(self, countries): + self.__enum('Country', countries, self.__country) + + def scripts(self, scripts): + self.__enum('Script', scripts, self.__script) + self.writer.write('\n') + + # Implementation details + from enumdata import (language_aliases as __language, + country_aliases as __country, + script_aliases as __script) + + def __enum(self, name, book, alias): + assert book + out, dupes = self.writer.write, self.__dupes + out(' enum {} {{\n'.format(name)) + for key, value in book.items(): + member = value[0] + if name == 'Script': + # Don't .capitalize() as some names are already camel-case (see enumdata.py): + member = ''.join(word[0].upper() + word[1:] for word in member.split()) + if not member.endswith('Script'): + member += 'Script' + if member in dupes: + raise Error('The script name "{}" is messy'.format(member)) + else: + member = ''.join(member.split()) + member = member + name if member in dupes else member + out(' {} = {},\n'.format(member, key)) + + out('\n ' + + ',\n '.join('{} = {}'.format(*pair) + for pair in sorted(alias.items())) + + ',\n\n Last{} = {}\n }};\n'.format(name, member)) + +def usage(name, err, message = ''): + err.write("""Usage: {} path/to/qlocale.xml root/of/qtbase +""".format(name)) # TODO: elaborate + if message: + err.write('\n' + message + '\n') + +def main(args, out, err): + # TODO: Make calendars a command-line parameter + # map { CLDR name: Qt file name } + calendars = {'gregorian': 'roman', 'persian': 'jalali', 'islamic': 'hijri',} # 'hebrew': 'hebrew', + + name = args.pop(0) + if len(args) != 2: + usage(name, err, 'I expect two arguments') + return 1 + + qlocalexml = args.pop(0) + qtsrcdir = args.pop(0) if not (os.path.isdir(qtsrcdir) and all(os.path.isfile(os.path.join(qtsrcdir, 'src', 'corelib', 'text', leaf)) for leaf in ('qlocale_data_p.h', 'qlocale.h', 'qlocale.qdoc'))): - usage() - - (data_temp_file, data_temp_file_path) = tempfile.mkstemp("qlocale_data_p", dir=qtsrcdir) - data_temp_file = os.fdopen(data_temp_file, "w") - qlocaledata_file = open(qtsrcdir + "/src/corelib/text/qlocale_data_p.h", "r") - s = qlocaledata_file.readline() - while s and s != GENERATED_BLOCK_START: - data_temp_file.write(s) - s = qlocaledata_file.readline() - data_temp_file.write(GENERATED_BLOCK_START) + usage(name, err, 'Missing expected files under qtbase source root ' + qtsrcdir) + return 1 reader = QLocaleXmlReader(qlocalexml) locale_map = dict(reader.loadLocaleMap(calendars, sys.stderr.write)) - data_temp_file.write(generated_template % (datetime.date.today(), reader.cldrVersion)) - - # Likely subtags map - data_temp_file.write("static const QLocaleId likely_subtags[] = {\n") - for had, have, got, give, last in reader.likelyMap(): - data_temp_file.write(' {{ {:3d}, {:3d}, {:3d} }}'.format(*have)) - data_temp_file.write(', {{ {:3d}, {:3d}, {:3d} }}'.format(*give)) - data_temp_file.write(' ' if last else ',') - data_temp_file.write(' // {} -> {}\n'.format(had, got)) - data_temp_file.write("};\n") - - data_temp_file.write("\n") - - # Locale index - data_temp_file.write("static const quint16 locale_index[] = {\n") - for index, name in reader.languageIndices(tuple(k[0] for k in locale_map)): - data_temp_file.write('{:6d}, // {}\n'.format(index, name)) - data_temp_file.write(" 0 // trailing 0\n") - data_temp_file.write("};\n\n") - - list_pattern_part_data = StringData('list_pattern_part_data') - date_format_data = StringData('date_format_data') - time_format_data = StringData('time_format_data') - days_data = StringData('days_data') - am_data = StringData('am_data') - pm_data = StringData('pm_data') - byte_unit_data = StringData('byte_unit_data') - currency_symbol_data = StringData('currency_symbol_data') - currency_display_name_data = StringData('currency_display_name_data') - currency_format_data = StringData('currency_format_data') - endonyms_data = StringData('endonyms_data') - - # Locale data - data_temp_file.write("static const QLocaleData locale_data[] = {\n") - # Table headings: keep each label centred in its field, matching line_format: - data_temp_file.write(' // ' - # Width 6 + comma: - + ' lang ' # IDs - + 'script ' - + ' terr ' - + ' dec ' # Numeric punctuation: - + ' group ' - + ' list ' # List delimiter - + ' prcnt ' # Arithmetic symbols: - + ' zero ' - + ' minus ' - + ' plus ' - + ' exp ' - # Width 8 + comma - to make space for these wide labels ! - + ' quotOpn ' # Quotation marks - + ' quotEnd ' - + 'altQtOpn ' - + 'altQtEnd ' - # Width 11 + comma: - + ' lpStart ' # List pattern - + ' lpMid ' - + ' lpEnd ' - + ' lpTwo ' - + ' sDtFmt ' # Date format - + ' lDtFmt ' - + ' sTmFmt ' # Time format - + ' lTmFmt ' - + ' ssDays ' # Days - + ' slDays ' - + ' snDays ' - + ' sDays ' - + ' lDays ' - + ' nDays ' - + ' am ' # am/pm indicators - + ' pm ' - # Width 8 + comma - + ' byte ' - + ' siQuant ' - + 'iecQuant ' - # Width 8+4 + comma - + ' currISO ' - # Width 11 + comma: - + ' currSym ' # Currency formatting: - + ' currDsply ' - + ' currFmt ' - + ' currFmtNeg ' - + ' endoLang ' # Name of language in itself, and of country: - + ' endoCntry ' - # Width 6 + comma: - + 'curDgt ' # Currency number representation: - + 'curRnd ' - + 'dow1st ' # First day of week - + ' wknd+ ' # Week-end start/end days: - + ' wknd-' - # No trailing space on last entry (be sure to - # pad before adding anything after it). - + '\n') locale_keys = locale_map.keys() compareLocaleKeys.default_map = dict(reader.defaultMap()) locale_keys.sort(compareLocaleKeys) - line_format = (' { ' - # Locale-identifier: - + '%6d,' * 3 - # Numeric formats, list delimiter: - + '%6d,' * 8 - # Quotation marks: - + '%8d,' * 4 - # List patterns, date/time formats, month/day names, am/pm: - + '%11s,' * 16 - # SI/IEC byte-unit abbreviations: - + '%8s,' * 3 - # Currency ISO code: - + ' %10s, ' - # Currency and endonyms - + '%11s,' * 6 - # Currency formatting: - + '%6d,%6d' - # Day of week and week-end: - + ',%6d' * 3 - + ' }') - for key in locale_keys: - l = locale_map[key] - data_temp_file.write(line_format - % (key[0], key[1], key[2], - l.decimal, - l.group, - l.listDelim, - l.percent, - l.zero, - l.minus, - l.plus, - l.exp, - l.quotationStart, - l.quotationEnd, - l.alternateQuotationStart, - l.alternateQuotationEnd, - list_pattern_part_data.append(l.listPatternPartStart), - list_pattern_part_data.append(l.listPatternPartMiddle), - list_pattern_part_data.append(l.listPatternPartEnd), - list_pattern_part_data.append(l.listPatternPartTwo), - date_format_data.append(l.shortDateFormat), - date_format_data.append(l.longDateFormat), - time_format_data.append(l.shortTimeFormat), - time_format_data.append(l.longTimeFormat), - days_data.append(l.standaloneShortDays), - days_data.append(l.standaloneLongDays), - days_data.append(l.standaloneNarrowDays), - days_data.append(l.shortDays), - days_data.append(l.longDays), - days_data.append(l.narrowDays), - am_data.append(l.am), - pm_data.append(l.pm), - byte_unit_data.append(l.byte_unit), - byte_unit_data.append(l.byte_si_quantified), - byte_unit_data.append(l.byte_iec_quantified), - currencyIsoCodeData(l.currencyIsoCode), - currency_symbol_data.append(l.currencySymbol), - currency_display_name_data.append(l.currencyDisplayName), - currency_format_data.append(l.currencyFormat), - currency_format_data.append(l.currencyNegativeFormat), - endonyms_data.append(l.languageEndonym), - endonyms_data.append(l.countryEndonym), - l.currencyDigits, - l.currencyRounding, - l.firstDayOfWeek, - l.weekendStart, - l.weekendEnd) - + ", // %s/%s/%s\n" % (l.language, l.script, l.country)) - data_temp_file.write(line_format # All zeros, matching the format: - % ( (0,) * (3 + 8 + 4) + ("0,0",) * (16 + 3) - + (currencyIsoCodeData(0),) - + ("0,0",) * 6 + (0,) * (2 + 3)) - + " // trailing 0s\n") - data_temp_file.write("};\n") - - # StringData tables: - for data in (list_pattern_part_data, date_format_data, - time_format_data, days_data, - byte_unit_data, am_data, pm_data, currency_symbol_data, - currency_display_name_data, currency_format_data, - endonyms_data): - data.write(data_temp_file) - - data_temp_file.write("\n") - - # Language name list - data_temp_file.write("static const char language_name_list[] =\n") - data_temp_file.write('"Default\\0"\n') - for key, value in reader.languages.items(): - if key == 0: - continue - data_temp_file.write('"' + value[0] + '\\0"\n') - data_temp_file.write(";\n") - - data_temp_file.write("\n") - - # Language name index - data_temp_file.write("static const quint16 language_name_index[] = {\n") - data_temp_file.write(" 0, // AnyLanguage\n") - index = 8 - for key, value in reader.languages.items(): - if key == 0: - continue - language = value[0] - data_temp_file.write("%6d, // %s\n" % (index, language)) - index += len(language) + 1 - data_temp_file.write("};\n") - - data_temp_file.write("\n") - - # Script name list - data_temp_file.write("static const char script_name_list[] =\n") - data_temp_file.write('"Default\\0"\n') - for key, value in reader.scripts.items(): - if key == 0: - continue - data_temp_file.write('"' + value[0] + '\\0"\n') - data_temp_file.write(";\n") - - data_temp_file.write("\n") - - # Script name index - data_temp_file.write("static const quint16 script_name_index[] = {\n") - data_temp_file.write(" 0, // AnyScript\n") - index = 8 - for key, value in reader.scripts.items(): - if key == 0: - continue - script = value[0] - data_temp_file.write("%6d, // %s\n" % (index, script)) - index += len(script) + 1 - data_temp_file.write("};\n") - - data_temp_file.write("\n") - - # Country name list - data_temp_file.write("static const char country_name_list[] =\n") - data_temp_file.write('"Default\\0"\n') - for key, value in reader.countries.items(): - if key == 0: - continue - data_temp_file.write('"' + value[0] + '\\0"\n') - data_temp_file.write(";\n") - - data_temp_file.write("\n") - - # Country name index - data_temp_file.write("static const quint16 country_name_index[] = {\n") - data_temp_file.write(" 0, // AnyCountry\n") - index = 8 - for key, value in reader.countries.items(): - if key == 0: - continue - country = value[0] - data_temp_file.write("%6d, // %s\n" % (index, country)) - index += len(country) + 1 - data_temp_file.write("};\n") - - data_temp_file.write("\n") - - # Language code list - data_temp_file.write("static const unsigned char language_code_list[] =\n") - for key, value in reader.languages.items(): - code = value[1] - if len(code) == 2: - code += r"\0" - data_temp_file.write('"%2s" // %s\n' % (code, value[0])) - data_temp_file.write(";\n") - - data_temp_file.write("\n") - - # Script code list - data_temp_file.write("static const unsigned char script_code_list[] =\n") - for key, value in reader.scripts.items(): - code = value[1] - for i in range(4 - len(code)): - code += "\\0" - data_temp_file.write('"%2s" // %s\n' % (code, value[0])) - data_temp_file.write(";\n") - - # Country code list - data_temp_file.write("static const unsigned char country_code_list[] =\n") - for key, value in reader.countries.items(): - code = value[1] - if len(code) == 2: - code += "\\0" - data_temp_file.write('"%2s" // %s\n' % (code, value[0])) - data_temp_file.write(";\n") - - data_temp_file.write("\n") - data_temp_file.write(GENERATED_BLOCK_END) - s = qlocaledata_file.readline() - # skip until end of the old block - while s and s != GENERATED_BLOCK_END: - s = qlocaledata_file.readline() - - s = qlocaledata_file.readline() - while s: - data_temp_file.write(s) - s = qlocaledata_file.readline() - data_temp_file.close() - qlocaledata_file.close() - - os.remove(qtsrcdir + "/src/corelib/text/qlocale_data_p.h") - os.rename(data_temp_file_path, qtsrcdir + "/src/corelib/text/qlocale_data_p.h") + try: + writer = LocaleDataWriter(os.path.join(qtsrcdir, 'src', 'corelib', 'text', + 'qlocale_data_p.h'), + qtsrcdir, reader.cldrVersion) + except IOError as e: + err.write('Failed to open files to transcribe locale data: ' + (e.message or e.args[1])) + return 1 + + try: + writer.likelySubtags(reader.likelyMap()) + writer.localeIndex(reader.languageIndices(tuple(k[0] for k in locale_map))) + writer.localeData(locale_map, locale_keys) + writer.writer.write('\n') + writer.languageNames(reader.languages) + writer.scriptNames(reader.scripts) + writer.countryNames(reader.countries) + # TODO: merge the next three into the previous three + writer.languageCodes(reader.languages) + writer.scriptCodes(reader.scripts) + writer.countryCodes(reader.countries) + except Error as e: + writer.cleanup() + err.write('\nError updating locale data: ' + e.message + '\n') + return 1 + + writer.close() # Generate calendar data - calendar_format = ' {%6d,%6d,%6d,{%5s},{%5s},{%5s},{%5s},{%5s},{%5s}}, ' for calendar, stem in calendars.items(): - months_data = StringData('months_data') - calendar_data_file = "q%scalendar_data_p.h" % stem - calendar_template_file = open(os.path.join(qtsrcdir, 'src', 'corelib', 'time', - calendar_data_file), "r") - (calendar_temp_file, calendar_temp_file_path) = tempfile.mkstemp(calendar_data_file, dir=qtsrcdir) - calendar_temp_file = os.fdopen(calendar_temp_file, "w") - s = calendar_template_file.readline() - while s and s != GENERATED_BLOCK_START: - calendar_temp_file.write(s) - s = calendar_template_file.readline() - calendar_temp_file.write(GENERATED_BLOCK_START) - calendar_temp_file.write(generated_template % (datetime.date.today(), reader.cldrVersion)) - calendar_temp_file.write("static const QCalendarLocale locale_data[] = {\n") - calendar_temp_file.write(' // ' - # IDs, width 7 (6 + comma) - + ' lang ' - + ' script' - + ' terr ' - # Month-name start-end pairs, width 8 (5 plus '{},'): - + ' sShort ' - + ' sLong ' - + ' sNarrow' - + ' short ' - + ' long ' - + ' narrow' - # No trailing space on last; be sure - # to pad before adding later entries. - + '\n') - for key in locale_keys: - l = locale_map[key] - calendar_temp_file.write( - calendar_format - % (key[0], key[1], key[2], - months_data.append(l.standaloneShortMonths[calendar]), - months_data.append(l.standaloneLongMonths[calendar]), - months_data.append(l.standaloneNarrowMonths[calendar]), - months_data.append(l.shortMonths[calendar]), - months_data.append(l.longMonths[calendar]), - months_data.append(l.narrowMonths[calendar])) - + "// %s/%s/%s\n " % (l.language, l.script, l.country)) - calendar_temp_file.write(calendar_format % ( (0,) * 3 + ('0,0',) * 6 ) - + '// trailing zeros\n') - calendar_temp_file.write("};\n") - months_data.write(calendar_temp_file) - s = calendar_template_file.readline() - while s and s != GENERATED_BLOCK_END: - s = calendar_template_file.readline() - while s: - calendar_temp_file.write(s) - s = calendar_template_file.readline() - os.rename(calendar_temp_file_path, - os.path.join(qtsrcdir, 'src', 'corelib', 'time', calendar_data_file)) + try: + writer = CalendarDataWriter(os.path.join(qtsrcdir, 'src', 'corelib', 'time', + 'q{}calendar_data_p.h'.format(stem)), + qtsrcdir, reader.cldrVersion) + except IOError as e: + err.write('Failed to open files to transcribe ' + calendar + + ' data ' + (e.message or e.args[1])) + return 1 + + try: + writer.write(calendar, locale_map, locale_keys) + except Error as e: + writer.cleanup() + err.write('\nError updating ' + calendar + ' locale data: ' + e.message + '\n') + return 1 + + writer.close() # qlocale.h + try: + writer = LocaleHeaderWriter(os.path.join(qtsrcdir, 'src', 'corelib', 'text', 'qlocale.h'), + qtsrcdir, reader.dupes) + except IOError as e: + err.write('Failed to open files to transcribe qlocale.h: ' + (e.message or e.args[1])) + return 1 - (qlocaleh_temp_file, qlocaleh_temp_file_path) = tempfile.mkstemp("qlocale.h", dir=qtsrcdir) - qlocaleh_temp_file = os.fdopen(qlocaleh_temp_file, "w") - qlocaleh_file = open(qtsrcdir + "/src/corelib/text/qlocale.h", "r") - s = qlocaleh_file.readline() - while s and s != GENERATED_BLOCK_START: - qlocaleh_temp_file.write(s) - s = qlocaleh_file.readline() - qlocaleh_temp_file.write(GENERATED_BLOCK_START) - qlocaleh_temp_file.write("// see qlocale_data_p.h for more info on generated data\n") - - # Language enum - qlocaleh_temp_file.write(" enum Language {\n") - language = None - for key, value in reader.languages.items(): - language = fixedLanguageName(value[0], reader.dupes) - qlocaleh_temp_file.write(" " + language + " = " + str(key) + ",\n") - - qlocaleh_temp_file.write("\n " + - ",\n ".join('%s = %s' % pair - for pair in sorted(language_aliases.items())) + - ",\n") - qlocaleh_temp_file.write("\n") - qlocaleh_temp_file.write(" LastLanguage = " + language + "\n") - qlocaleh_temp_file.write(" };\n") - - qlocaleh_temp_file.write("\n") - - # Script enum - qlocaleh_temp_file.write(" enum Script {\n") - script = None - for key, value in reader.scripts.items(): - script = fixedScriptName(value[0], reader.dupes) - qlocaleh_temp_file.write(" " + script + " = " + str(key) + ",\n") - qlocaleh_temp_file.write("\n " + - ",\n ".join('%s = %s' % pair - for pair in sorted(script_aliases.items())) + - ",\n") - qlocaleh_temp_file.write("\n") - qlocaleh_temp_file.write(" LastScript = " + script + "\n") - qlocaleh_temp_file.write(" };\n") - - # Country enum - qlocaleh_temp_file.write(" enum Country {\n") - country = None - for key, value in reader.countries.items(): - country = fixedCountryName(value[0], reader.dupes) - qlocaleh_temp_file.write(" " + country + " = " + str(key) + ",\n") - qlocaleh_temp_file.write("\n " + - ",\n ".join('%s = %s' % pair - for pair in sorted(country_aliases.items())) + - ",\n") - qlocaleh_temp_file.write("\n") - qlocaleh_temp_file.write(" LastCountry = " + country + "\n") - qlocaleh_temp_file.write(" };\n") - - qlocaleh_temp_file.write(GENERATED_BLOCK_END) - s = qlocaleh_file.readline() - # skip until end of the old block - while s and s != GENERATED_BLOCK_END: - s = qlocaleh_file.readline() - - s = qlocaleh_file.readline() - while s: - qlocaleh_temp_file.write(s) - s = qlocaleh_file.readline() - qlocaleh_temp_file.close() - qlocaleh_file.close() - - os.remove(qtsrcdir + "/src/corelib/text/qlocale.h") - os.rename(qlocaleh_temp_file_path, qtsrcdir + "/src/corelib/text/qlocale.h") + try: + writer.languages(reader.languages) + writer.scripts(reader.scripts) + writer.countries(reader.countries) + except Error as e: + writer.cleanup() + err.write('\nError updating qlocale.h: ' + e.message + '\n') + return 1 + + writer.close() # qlocale.qdoc + try: + writer = Transcriber(os.path.join(qtsrcdir, 'src', 'corelib', 'text', 'qlocale.qdoc'), + qtsrcdir) + except IOError as e: + err.write('Failed to open files to transcribe qlocale.qdoc: ' + (e.message or e.args[1])) + return 1 - (qlocaleqdoc_temp_file, qlocaleqdoc_temp_file_path) = tempfile.mkstemp("qlocale.qdoc", dir=qtsrcdir) - qlocaleqdoc_temp_file = os.fdopen(qlocaleqdoc_temp_file, "w") - qlocaleqdoc_file = open(qtsrcdir + "/src/corelib/text/qlocale.qdoc", "r") - s = qlocaleqdoc_file.readline() DOCSTRING = " QLocale's data is based on Common Locale Data Repository " - while s: - if DOCSTRING in s: - qlocaleqdoc_temp_file.write(DOCSTRING + "v" + reader.cldrVersion + ".\n") - else: - qlocaleqdoc_temp_file.write(s) - s = qlocaleqdoc_file.readline() - qlocaleqdoc_temp_file.close() - qlocaleqdoc_file.close() - - os.remove(qtsrcdir + "/src/corelib/text/qlocale.qdoc") - os.rename(qlocaleqdoc_temp_file_path, qtsrcdir + "/src/corelib/text/qlocale.qdoc") + try: + for line in writer.reader: + if DOCSTRING in line: + writer.writer.write(DOCSTRING + 'v' + reader.cldrVersion + '.\n') + else: + writer.writer.write(line) + except Error as e: + writer.cleanup() + err.write('\nError updating qlocale.qdoc: ' + e.message + '\n') + return 1 + + writer.close() + return 0 if __name__ == "__main__": - main() + import sys + sys.exit(main(sys.argv, sys.stdout, sys.stderr)) -- cgit v1.2.3 From c834dbc6fb8881f543e2a599afbc23ee1277483d Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Thu, 27 Feb 2020 10:56:36 +0100 Subject: Move cldr2qtimezone.py's CLDR-reading to a CldrAccess class This begins the process of replacing xpathlite.py, adding low-level DOM-access classes to ldml.py and the CldrAccess class to cldr.py Moved a format comment from cldr2qtimezone.py's doc-string to the method of CldrAccess that does the actual reading. Task-number: QTBUG-81344 Change-Id: I46ae3f402f8207ced6d30a1de5cedaeef47b2bcf Reviewed-by: Cristian Maureira-Fredes --- util/locale_database/cldr.py | 182 +++++++++++++++++++++++++++++++++ util/locale_database/cldr2qtimezone.py | 88 +++------------- util/locale_database/ldml.py | 140 +++++++++++++++++++++++++ 3 files changed, 339 insertions(+), 71 deletions(-) create mode 100644 util/locale_database/cldr.py create mode 100644 util/locale_database/ldml.py diff --git a/util/locale_database/cldr.py b/util/locale_database/cldr.py new file mode 100644 index 0000000000..7890adf307 --- /dev/null +++ b/util/locale_database/cldr.py @@ -0,0 +1,182 @@ +############################################################################# +## +## Copyright (C) 2020 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# +"""Digesting the CLDR's data. + +Provides two class: + CldrAccess -- used by the reader to access the tree of data files + +The former should normally be all you need to access. +See individual classes for further detail. +""" + +from xml.dom import minidom +from weakref import WeakValueDictionary as CacheDict +import os + +from localetools import Error +from ldml import Node, Supplement + +class CldrAccess (object): + def __init__(self, root): + """Set up a master object for accessing CLDR data. + + Single parameter, root, is the file-system path to the root of + the unpacked CLDR archive; its common/ sub-directory should + contain dtd/, main/ and supplemental/ sub-directories.""" + self.root = root + + def supplement(self, name): + """Loads supplemental data as a Supplement object. + + The name should be that of a file in common/supplemental/, without path. + """ + return Supplement(Node(self.__xml(('common', 'supplemental', name)))) + + def readWindowsTimeZones(self, lookup): # For use by cldr2qtimezone.py + """Digest CLDR's MS-Win time-zone name mapping. + + MS-Win have their own eccentric names for time-zones. CLDR + helpfully provides a translation to more orthodox names. + + Singe argument, lookup, is a mapping from known MS-Win names + for locales to a unique integer index (starting at 1). + + The XML structure we read has the form: + + + + + + + + + + + + +""" + zones = self.supplement('windowsZones.xml') + enum = self.__enumMap('country') + badZones, unLands, defaults, windows = set(), set(), {}, {} + + for name, attrs in zones.find('windowsZones/mapTimezones'): + if name != 'mapZone': + continue + + wid, code = attrs['other'], attrs['territory'] + data = dict(windowsId = wid, + countryCode = code, + ianaList = attrs['type']) + + try: + key = lookup[wid] + except KeyError: + badZones.add(wid) + key = 0 + data['windowsKey'] = key + + if code == u'001': + defaults[key] = data['ianaList'] + else: + try: + cid, name = enum[code] + except KeyError: + unLands.append(code) + continue + data.update(countryId = cid, country = name) + windows[key, cid] = data + + if unLands: + raise Error('Unknown country codes, please add to enumdata.py: ' + + ', '.join(sorted(unLands))) + + if badZones: + raise Error('Unknown Windows IDs, please add to cldr2qtimezone.py: ' + + ', '.join(sorted(badZones))) + + return self.cldrVersion, defaults, windows + + @property + def cldrVersion(self): + # Evaluate so as to ensure __cldrVersion is set: + self.__scanLdmlDtd() + return self.__cldrVersion + + # Implementation details + def __xml(self, path, cache = CacheDict(), read = minidom.parse, joinPath = os.path.join): + try: + doc = cache[path] + except KeyError: + cache[path] = doc = read(joinPath(self.root, *path)).documentElement + return doc + + def __open(self, path, joinPath=os.path.join): + return open(joinPath(self.root, *path)) + + @property + def __supplementalData(self, cache = []): + if not cache: + cache.append(self.supplement('supplementalData.xml')) + return cache[0] + + def __scanLdmlDtd(self, joinPath = os.path.join): + """Scan the LDML DTD, record CLDR version.""" + with self.__open(('common', 'dtd', 'ldml.dtd')) as dtd: + for line in dtd: + if line.startswith(' might be stuck on its end: + self.__cldrVersion = parts[5].split('"')[1] + break + + def __enumMap(self, key, cache = {}): + if not cache: + cache['variant'] = {'': (0, 'This should never be seen outside ldml.py')} + # They're not actually lists: mappings from numeric value + # to pairs of full name and short code. What we want, in + # each case, is a mapping from code to the other two. + from enumdata import language_list, script_list, country_list + for form, book, empty in (('language', language_list, 'AnyLanguage'), + ('script', script_list, 'AnyScript'), + ('country', country_list, 'AnyCountry')): + cache[form] = dict((pair[1], (num, pair[0])) + for num, pair in book.items() if pair[0] != 'C') + # (Have to filter out the C locale, as we give it the + # same (all space) code as AnyLanguage, whose code + # should probably be 'und' instead.) + + # Map empty to zero and the any value: + cache[form][''] = (0, empty) + # and map language code 'und' also to (0, any): + cache['language']['und'] = (0, 'AnyLanguage') + + return cache[key] + +# Unpolute the namespace: we don't need to export these. +del minidom, CacheDict, os diff --git a/util/locale_database/cldr2qtimezone.py b/util/locale_database/cldr2qtimezone.py index f2d2003d53..70b5d1e69e 100755 --- a/util/locale_database/cldr2qtimezone.py +++ b/util/locale_database/cldr2qtimezone.py @@ -34,32 +34,15 @@ the CLDR data. Pass its common/ directory as first parameter to this script and the qtbase root directory as second parameter. It shall update qtbase's src/corelib/time/qtimezoneprivate_data_p.h ready for use. - -The XML structure we read has the form: - - - - - - - - - - - - - - """ import os import re import datetime +import textwrap -import enumdata from localetools import unicode2hex, wrap_list, Error, SourceFileEditor -from xpathlite import DraftResolution, findAlias, findEntry, findTagsInFile, \ - _findEntryInFile as findEntryInFile +from cldr import CldrAccess ### Data that may need updates in response to new entries in the CLDR file ### @@ -351,10 +334,10 @@ def main(args, out, err): """Parses CLDR's data and updates Qt's representation of it. Takes sys.argv, sys.stdout, sys.stderr (or equivalents) as - arguments. Expects two command-line options: the common/ - subdirectory of the unpacked CLDR data-file tree and the root of - the qtbase module's checkout. Updates QTimeZone's private data - about Windows time-zone IDs.""" + arguments. Expects two command-line options: the root of the + unpacked CLDR data-file tree and the root of the qtbase module's + checkout. Updates QTimeZone's private data about Windows time-zone + IDs.""" name = args.pop(0) if len(args) != 2: usage(err, name, "Expected two arguments") @@ -375,54 +358,17 @@ def main(args, out, err): usage(err, name, 'No such file: ' + dataFilePath) return 1 - windowsZonesPath = cldrPath + "/supplemental/windowsZones.xml" - if not os.path.isfile(windowsZonesPath): - usage(err, name, 'Failed to find CLDR data file: ' + windowsZonesPath) - return 1 - - cldrVersion = 'unknown' - ldml = open(cldrPath + "/dtd/ldml.dtd", "r") - for line in ldml: - if 'version cldrVersion CDATA #FIXED' in line: - cldrVersion = line.split('"')[1] - - mapTimezones = findTagsInFile(windowsZonesPath, "windowsZones/mapTimezones") - if not mapTimezones: - err.write('Failed to find time-zone data - aborting !\n') + try: + version, defaults, winIds = CldrAccess(cldrPath).readWindowsTimeZones( + dict((name, ind) for ind, name in enumerate((x[0] for x in windowsIdList), 1))) + except IOError as e: + usage(err, name, + 'Failed to open common/supplemental/windowsZones.xml: ' + (e.message or e.args[1])) return 1 - - defaultDict, windowsIdDict = {}, {} - badZones = set() - winIdToIndex = dict((name, ind + 1) for ind, name in enumerate(x[0] for x in windowsIdList)) - for mapZone in mapTimezones: - # [u'mapZone', [(u'territory', u'MH'), (u'other', u'UTC+12'), (u'type', u'Pacific/Majuro Pacific/Kwajalein')]] - if mapZone[0] == u'mapZone': - data = {} - for attribute in mapZone[1]: - if attribute[0] == u'other': - data['windowsId'] = attribute[1] - if attribute[0] == u'territory': - data['countryCode'] = attribute[1] - if attribute[0] == u'type': - data['ianaList'] = attribute[1] - - try: - data['windowsKey'] = winIdToIndex[data['windowsId']] - except KeyError: - badZones.add(data['windowsId']) - - countryId = 0 - if data['countryCode'] == u'001': - defaultDict[data['windowsKey']] = data['ianaList'] - else: - data['countryId'] = enumdata.countryCodeToId(data['countryCode']) - if data['countryId'] < 0: - raise Error('Unknown Country Code "{}"'.format(data['countryCode'])) - data['country'] = enumdata.country_list[data['countryId']][0] - windowsIdDict[data['windowsKey'], data['countryId']] = data - if badZones: - err.write('\n\t'.join(["\nUnknown Windows ID, please add:"] + sorted(badZones)) - + "\nto the windowsIdList in cldr2qtimezone.py\n\n") + except Error as e: + err.write('\n'.join(textwrap.wrap( + 'Failed to read windowsZones.xml: ' + (e.message or e.args[1]), + subsequent_indent=' ', width=80)) + '\n') return 1 out.write('Input file parsed, now writing data\n') @@ -433,7 +379,7 @@ def main(args, out, err): return 1 try: - writer.write(cldrVersion, defaultDict, windowsIdDict) + writer.write(version, defaults, winIds) except Error as e: writer.cleanup() err.write('\nError in Windows ID data: ' + e.message + '\n') diff --git a/util/locale_database/ldml.py b/util/locale_database/ldml.py new file mode 100644 index 0000000000..4aaa728a86 --- /dev/null +++ b/util/locale_database/ldml.py @@ -0,0 +1,140 @@ +############################################################################# +## +## Copyright (C) 2020 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# +"""Parsing the Locale Data Markup Language + +It's an XML format, so the raw parsing of XML is, of course, delegated +to xml.dom.minidom; but it has its own specific schemata and some +funky rules for combining data from various files (inheritance between +locales). The use of it we're interested in is extraction of CLDR's +data, so some of the material here is specific to CLDR; see cldr.py +for how it is mainly used. + +Provides various classes to wrap xml.dom's objects, specifically those +returned by minidom.parse() and their child-nodes: + Node -- wraps any node in the DOM tree + XmlScanner -- wraps the root element of a stand-alone XML file + Supplement -- specializes XmlScanner for supplemental data files + +See individual classes for further detail. +""" +from localetools import Error + +class Node (object): + """Wrapper for an arbitrary DOM node. + + Provides various ways to select chldren of a node. Selected child + nodes are returned wrapped as Node objects. A Node exposes the + raw DOM node it wraps via its .dom attribute.""" + + def __init__(self, elt): + """Wraps a DOM node for ease of access. + + Single argument, elt, is the DOM node to wrap.""" + self.dom = elt + + def findAllChildren(self, tag, wanted = None): + """All children that do have the given tag and attributes. + + First argument is the tag: children with any other tag are + ignored. + + Optional second argument, wanted, should either be None or map + attribute names to the values they must have. Only child nodes + with these attributes set to the given values are yielded.""" + + cutoff = 4 # Only accept approved, for now + for child in self.dom.childNodes: + if child.nodeType != child.ELEMENT_NODE: + continue + if child.nodeName != tag: + continue + + try: + draft = child.attributes['draft'] + except KeyError: + pass + else: + if self.__draftScores.get(draft, 0) < cutoff: + continue + + if wanted is not None: + try: + if wanted and any(child.attributes[k].nodeValue != v for k, v in wanted.items()): + continue + except KeyError: # Some wanted attribute is missing + continue + + yield Node(child) + + __draftScores = dict(true = 0, unconfirmed = 1, provisional = 2, + contributed = 3, approved = 4, false = 4) + +def _parseXPath(selector): + # Split "tag[attr=val][...]" into tag-name and attribute mapping + attrs = selector.split('[') + name = attrs.pop(0) + if attrs: + attrs = [x.strip() for x in attrs] + assert all(x.endswith(']') for x in attrs) + attrs = [x[:-1].split('=') for x in attrs] + assert all(len(x) in (1, 2) for x in attrs) + attrs = (('type', x[0]) if len(x) == 1 else x for x in attrs) + return name, dict(attrs) + +def _iterateEach(iters): + # Flatten a two-layer iterator. + for it in iters: + for item in it: + yield item + +class XmlScanner (object): + """Wrap an XML file to enable XPath access to its nodes. + """ + def __init__(self, node): + self.root = node + + def findNodes(self, xpath): + """Return all nodes under self.root matching this xpath""" + elts = (self.root,) + for selector in xpath.split('/'): + tag, attrs = _parseXPath(selector) + elts = tuple(_iterateEach(e.findAllChildren(tag, attrs) for e in elts)) + if not elts: + break + return elts + +class Supplement (XmlScanner): + # Replaces xpathlite.findTagsInFile() + def find(self, xpath): + elts = self.findNodes(xpath) + for elt in _iterateEach(e.dom.childNodes if e.dom.childNodes else (e.dom,) + for e in elts): + if elt.attributes: + yield (elt.nodeName, + dict((k, v if isinstance(v, basestring) else v.nodeValue) + for k, v in elt.attributes.items())) -- cgit v1.2.3 From be3dfd7a71a276b10bac50075b26c6af58b9d02b Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Thu, 27 Feb 2020 13:58:58 +0100 Subject: Rework cldr2qlocalexml.py's reading of CLDR data Move the code out to a CldrReader class in cldr.py, expand CldrAccess with facilities that needs, expand ldml.py to include support for more features, finally making xpathlite.py redundant. This initial commit aims, though, to be bug-for-bug compatible with xpathlite in its reading of the CLDR data. It turns out we've been using draftier data than we were aware of (which might not be a bad thing). The xpathlite code appeared to check for draft attributes, but these only appear on leaf nodes and most data were fetched by finding a parent and then scanning its children without the draft check; only am/pm data was actually being excluded based on draft values. (We allowed contributed, for am/pm, in addition to approved, which is all the xpathlite code allows otherwise.) There are also some less equivocal bugs; I'll deal with these in later commits. Simplified number-system data look-ups; the old get_number_in_system() was taking care of old LDML versions' placement of the number system attribute; this is no longer needed. (It was also being used for a currency value to which it was not appropriate, which is now handled separately; this is one of the bugs mentioned above.) Ditched a fall-back to nativeZeroDigit, which no longer exists in CLDR. Change the command-line to take the root of the CLDR data tree, rather than its common/main/ sub-directory. Support naming the file to which to write output, as a second command-line argument, instead of always writing to stdout (which remains the default) and leaving whoever runs the script to redirect stdout. Support (internally for now, while adding TODOs to give main() more command-line options) separating the stderr output into its more and less interesting parts; for now, continue producing both, but suppress the least interesting entirely. Task-number: QTBUG-81344 Change-Id: Ie611b47403a9452b51feaeeaaa0fbc8f7e84dc71 Reviewed-by: Cristian Maureira-Fredes --- util/locale_database/cldr.py | 498 ++++++++++++++++++++++++- util/locale_database/cldr2qlocalexml.py | 636 +++----------------------------- util/locale_database/ldml.py | 450 +++++++++++++++++++++- util/locale_database/qlocalexml2cpp.py | 2 +- util/locale_database/xpathlite.py | 284 -------------- 5 files changed, 972 insertions(+), 898 deletions(-) delete mode 100644 util/locale_database/xpathlite.py diff --git a/util/locale_database/cldr.py b/util/locale_database/cldr.py index 7890adf307..94459b9e3f 100644 --- a/util/locale_database/cldr.py +++ b/util/locale_database/cldr.py @@ -27,7 +27,8 @@ ############################################################################# """Digesting the CLDR's data. -Provides two class: +Provides two classes: + CldrReader -- driver for reading CLDR data CldrAccess -- used by the reader to access the tree of data files The former should normally be all you need to access. @@ -38,9 +39,206 @@ from xml.dom import minidom from weakref import WeakValueDictionary as CacheDict import os -from localetools import Error -from ldml import Node, Supplement +from ldml import Error, Node, XmlScanner, Supplement, LocaleScanner +from qlocalexml import Locale +class CldrReader (object): + def __init__(self, root, grumble = lambda msg: None, whitter = lambda msg: None): + """Set up a reader object for reading CLDR data. + + Single parameter, root, is the file-system path to the root of + the unpacked CLDR archive; its common/ sub-directory should + contain dtd/, main/ and supplemental/ sub-directories. + + Optional second argument, grumble, is a callable that logs + warnings and complaints, e.g. sys.stderr.write would be a + suitable callable. The default is a no-op that ignores its + single argument. Optional third argument is similar, used for + less interesting output; pass sys.stderr.write for it for + verbose output.""" + self.root = CldrAccess(root) + self.whitter, self.grumble = whitter, grumble + + def likelySubTags(self): + """Generator for likely subtag information. + + Yields pairs (have, give) of 4-tuples; if what you have + matches the left member, giving the right member is probably + sensible. Each 4-tuple's entries are the full names of a + language, a script, a country (strictly territory) and a + variant (currently ignored).""" + skips = [] + for got, use in self.root.likelySubTags(): + try: + have = self.__parseTags(got) + give = self.__parseTags(use) + except Error as e: + if ((use.startswith(got) or got.startswith('und_')) + and e.message.startswith('Unknown ') and ' code ' in e.message): + skips.append(use) + else: + self.grumble('Skipping likelySubtag "{}" -> "{}" ({})\n'.format(got, use, e.message)) + continue + if all(code.startswith('Any') and code[3].isupper() for code in have[:-1]): + continue + + give = (give[0], + # Substitute according to http://www.unicode.org/reports/tr35/#Likely_Subtags + have[1] if give[1] == 'AnyScript' else give[1], + have[2] if give[2] == 'AnyCountry' else give[2], + give[3]) # AnyVariant similarly ? + + yield have, give + + if skips: + # TODO: look at LDML's reserved locale tag names; they + # show up a lot in this, and may be grounds for filtering + # more out. + pass # self.__wrapped(self.whitter, 'Skipping likelySubtags (for unknown codes): ', skips) + + def readLocales(self, calendars = ('gregorian',)): + locales = tuple(self.__allLocales(calendars)) + return dict(((k.language_id, k.script_id, k.country_id, k.variant_code), + k) for k in locales) + + def __allLocales(self, calendars): + def skip(locale, reason): + return 'Skipping defaultContent locale "{}" ({})\n'.format(locale, reason) + + for locale in self.root.defaultContentLocales: + try: + language, script, country, variant = self.__splitLocale(locale) + except ValueError: + self.whitter(skip(locale, 'only language tag')) + continue + + if not (script or country): + self.grumble(skip(locale, 'second tag is neither script nor territory')) + continue + + if not (language and country): + continue + + try: + yield self.__getLocaleData(self.root.locale(locale), calendars, + language, script, country, variant) + except Error as e: + self.grumble(skip(locale, e.message)) + + for locale in self.root.fileLocales: + try: + chain = self.root.locale(locale) + language, script, country, variant = chain.tagCodes() + assert language + # TODO: this skip should probably be based on likely + # sub-tags, instead of empty country: if locale has a + # likely-subtag expansion, that's what QLocale uses, + # and we'll be saving its data for the expanded locale + # anyway, so don't need to record it for itself. + # See also QLocaleXmlReader.loadLocaleMap's grumble. + if not country: + continue + yield self.__getLocaleData(chain, calendars, language, script, country, variant) + except Error as e: + self.grumble('Skipping file locale "{}" ({})\n'.format(locale, e.message)) + + import textwrap + @staticmethod + def __wrapped(writer, prefix, tokens, wrap = textwrap.wrap): + writer('\n'.join(wrap(prefix + ', '.join(tokens), + subsequent_indent=' ', width=80)) + '\n') + del textwrap + + def __parseTags(self, locale): + tags = self.__splitLocale(locale) + language = tags.next() + script = country = variant = '' + try: + script, country, variant = tags + except ValueError: + pass + return tuple(p[1] for p in self.root.codesToIdName(language, script, country, variant)) + + def __splitLocale(self, name): + """Generate (language, script, territory, variant) from a locale name + + Ignores any trailing fields (with a warning), leaves script (a + capitalised four-letter token), territory (either a number or + an all-uppercase token) or variant (upper case and digits) + empty if unspecified. Only generates one entry if name is a + single tag (i.e. contains no underscores). Always yields 1 or + 4 values, never 2 or 3.""" + tags = iter(name.split('_')) + yield tags.next() # Language + tag = tags.next() # may raise StopIteration + + # Script is always four letters, always capitalised: + if len(tag) == 4 and tag[0].isupper() and tag[1:].islower(): + yield tag + try: + tag = tags.next() + except StopIteration: + tag = '' + else: + yield '' + + # Territory is upper-case or numeric: + if tag and tag.isupper() or tag.isdigit(): + yield tag + try: + tag = tags.next() + except StopIteration: + tag = '' + else: + yield '' + + # Variant can be any mixture of upper-case and digits. + if tag and all(c.isupper() or c.isdigit() for c in tag): + yield tag + tag = '' + else: + yield '' + + # If nothing is left, StopIteration will avoid the warning: + if not tag: + tag = tags.next() + self.grumble('Ignoring unparsed cruft {} in {}\n'.format('_'.join(tag + tuple(tags)), name)) + + def __getLocaleData(self, scan, calendars, language, script, country, variant): + ids, names = zip(*self.root.codesToIdName(language, script, country, variant)) + assert ids[0] > 0 and ids[2] > 0, (language, script, country, variant) + locale = Locale( + language = names[0], language_code = language, language_id = ids[0], + script = names[1], script_code = script, script_id = ids[1], + country = names[2], country_code = country, country_id = ids[2], + variant_code = variant) + + firstDay, weStart, weEnd = self.root.weekData(country) + assert all(day in ('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun') + for day in (firstDay, weStart, weEnd)) + + locale.update(firstDayOfWeek = firstDay, + weekendStart = weStart, + weekendEnd = weEnd) + + iso, digits, rounding = self.root.currencyData(country) + locale.update(currencyIsoCode = iso, + currencyDigits = int(digits), + currencyRounding = int(rounding)) + + locale.update(scan.currencyData(iso)) + locale.update(scan.numericData(self.root.numberSystem, self.whitter)) + locale.update(scan.textPatternData()) + locale.update(scan.endonyms(language, script, country, variant)) + locale.update(scan.unitData()) # byte, kB, MB, GB, ..., KiB, MiB, GiB, ... + locale.update(scan.calendarNames(calendars)) # Names of days and months + + return locale + +# Note: various caches assume this class is a singleton, so the +# "default" value for a parameter no caller should pass can serve as +# the cache. If a process were to instantiate this class with distinct +# roots, each cache would be filled by the first to need it ! class CldrAccess (object): def __init__(self, root): """Set up a master object for accessing CLDR data. @@ -50,6 +248,12 @@ class CldrAccess (object): contain dtd/, main/ and supplemental/ sub-directories.""" self.root = root + def xml(self, *path): + """Load a single XML file and return its root element as an XmlScanner. + + The path is interpreted relative to self.root""" + return XmlScanner(Node(self.__xml(path))) + def supplement(self, name): """Loads supplemental data as a Supplement object. @@ -57,6 +261,117 @@ class CldrAccess (object): """ return Supplement(Node(self.__xml(('common', 'supplemental', name)))) + def locale(self, name): + """Loads all data for a locale as a LocaleScanner object. + + The name should be a locale name; adding suffix '.xml' to it + should usually yield a file in common/main/. The returned + LocaleScanner object packages this file along with all those + from which it inherits; its methods know how to handle that + inheritance, where relevant.""" + return LocaleScanner(name, self.__localeRoots(name), self.__rootLocale) + + @property + def fileLocales(self, joinPath = os.path.join, listDirectory = os.listdir, + splitExtension = os.path.splitext): + """Generator for locale IDs seen in file-names. + + All *.xml other than root.xml in common/main/ are assumed to + identify locales.""" + for name in listDirectory(joinPath(self.root, 'common', 'main')): + stem, ext = splitExtension(name) + if ext == '.xml' and stem != 'root': + yield stem + + @property + def defaultContentLocales(self): + """Generator for the default content locales.""" + for name, attrs in self.supplement('supplementalMetadata.xml').find('metadata/defaultContent'): + try: + locales = attrs['locales'] + except KeyError: + pass + else: + for locale in locales.split(): + yield locale + + def likelySubTags(self): + for ignore, attrs in self.supplement('likelySubtags.xml').find('likelySubtags'): + yield attrs['from'], attrs['to'] + + def numberSystem(self, system): + """Get a description of a numbering system. + + Returns a mapping, with keys u'digits', u'type' and u'id'; the + value for this last is system. Raises KeyError for unknown + number system, ldml.Error on failure to load data.""" + try: + return self.__numberSystems[system] + except KeyError: + raise Error('Unsupported number system: {}'.format(system)) + + def weekData(self, country): + """Data on the weekly cycle. + + Returns a triple (W, S, E) of en's short names for week-days; + W is the first day of the week, S the start of the week-end + and E the end of the week-end. Where data for a country is + unavailable, the data for CLDR's territory 001 (The World) is + used.""" + try: + return self.__weekData[country] + except KeyError: + return self.__weekData['001'] + + def currencyData(self, country): + """Returns currency data for the given country code. + + Return value is a tuple (ISO4217 code, digit count, rounding + mode). If CLDR provides no data for this country, ('', 2, 1) + is the default result. + """ + try: + return self.__currencyData[country] + except KeyError: + return '', 2, 1 + + def codesToIdName(self, language, script, country, variant = ''): + """Maps each code to the appropriate ID and name. + + Returns a 4-tuple of (ID, name) pairs corresponding to the + language, script, country and variant given. Raises a + suitable error if any of them is unknown, indicating all that + are unknown plus suitable names for any that could sensibly be + added to enumdata.py to make them known. + + Until we implement variant support (QTBUG-81051), the fourth + member of the returned tuple is always 0 paired with a string + that should not be used.""" + enum = self.__enumMap + try: + return (enum('language')[language], + enum('script')[script], + enum('country')[country], + enum('variant')[variant]) + except KeyError: + pass + + parts, values = [], [language, script, country, variant] + for index, key in enumerate(('language', 'script', 'country', 'variant')): + naming, enums = self.__codeMap(key), enum(key) + value = values[index] + if value not in enums: + text = '{} code {}'.format(key, value) + name = naming.get(value) + if name and value != 'POSIX': + text += u' (could add {})'.format(name) + parts.append(text) + if len(parts) > 1: + parts[-1] = 'and ' + parts[-1] + assert parts + raise Error('Unknown ' + ', '.join(parts), + language, script, country, variant) + def readWindowsTimeZones(self, lookup): # For use by cldr2qtimezone.py """Digest CLDR's MS-Win time-zone name mapping. @@ -138,12 +453,98 @@ class CldrAccess (object): def __open(self, path, joinPath=os.path.join): return open(joinPath(self.root, *path)) + @property + def __rootLocale(self, cache = []): + if not cache: + cache.append(self.xml('common', 'main', 'root.xml')) + return cache[0] + @property def __supplementalData(self, cache = []): if not cache: cache.append(self.supplement('supplementalData.xml')) return cache[0] + @property + def __numberSystems(self, cache = {}, joinPath=os.path.join): + if not cache: + for ignore, attrs in self.supplement('numberingSystems.xml').find('numberingSystems'): + if ord(attrs.get('digits', u'\x10000')[0]) > 0xffff: + # FIXME, QTBUG-69324: make this redundant: + # omit number system if zero doesn't fit in single-char16 UTF-16 :-( + continue + + cache[attrs['id']] = attrs + assert cache + return cache + + @property + def __weekData(self, cache = {}): + if not cache: + firstDay, weStart, weEnd = self.__getWeekData() + # Massage those into an easily-consulted form: + # World defaults given for code '001': + mon, sat, sun = firstDay['001'], weStart['001'], weEnd['001'] + lands = set(firstDay) | set(weStart) | set(weEnd) + cache.update((land, + (firstDay.get(land, mon), weStart.get(land, sat), weEnd.get(land, sun))) + for land in lands) + assert cache + return cache + + def __getWeekData(self): + """Scan for data on the weekly cycle. + + Yields three mappings from locales to en's short names for + week-days; if a locale isn't a key of a given mapping, it + should use the '001' (world) locale's value. The first mapping + gives the day on which the week starts, the second gives the + day on which the week-end starts, the third gives the last day + of the week-end.""" + source = self.__supplementalData + for key in ('firstDay', 'weekendStart', 'weekendEnd'): + result = {} + for ignore, attrs in source.find('weekData/' + key): + assert ignore == key + day = attrs['day'] + assert day in ('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'), day + if 'alt' in attrs: + continue + for loc in attrs.get('territories', '').split(): + result[loc] = day + yield result + + @property + def __currencyData(self, cache = {}): + if not cache: + source = self.__supplementalData + for elt in source.findNodes('currencyData/region'): + iso, digits, rounding = '', 2, 1 + try: + country = elt.dom.attributes['iso3166'].nodeValue + except KeyError: + continue + for child in elt.findAllChildren('currency'): + try: + if child.dom.attributes['tender'].nodeValue == 'false': + continue + except KeyError: + pass + try: + child.dom.attributes['to'] # Is set if this element has gone out of date. + except KeyError: + iso = child.dom.attributes['iso4217'].nodeValue + break + if iso: + for tag, data in source.find( + 'currencyData/fractions/info[iso4217={}]'.format(iso)): + digits = data['digits'] + rounding = data['rounding'] + cache[country] = iso, digits, rounding + assert cache + + return cache + def __scanLdmlDtd(self, joinPath = os.path.join): """Scan the LDML DTD, record CLDR version.""" with self.__open(('common', 'dtd', 'ldml.dtd')) as dtd: @@ -151,7 +552,8 @@ class CldrAccess (object): if line.startswith(' might be stuck on its end: + # parts[5] is the version, in quotes, maybe + # with a final > attached to its end: self.__cldrVersion = parts[5].split('"')[1] break @@ -178,5 +580,93 @@ class CldrAccess (object): return cache[key] + def __codeMap(self, key, cache = {}, + # Maps our name for it to CLDR's name: + naming = {'language': 'languages', 'script': 'scripts', + 'country': 'territories', 'variant': 'variants'}): + if not cache: + root = self.xml('common', 'main', 'en.xml').root.findUniqueChild('localeDisplayNames') + for dst, src in naming.items(): + cache[dst] = dict(self.__codeMapScan(root.findUniqueChild(src))) + assert cache + + return cache[key] + + def __codeMapScan(self, node): + """Get mapping from codes to element values. + + Passed in node is a , , or + node, each child of which is a , +