diff --git a/.github/autobuild/ios.sh b/.github/autobuild/ios.sh index 011379fe5f..a9f7ace3eb 100755 --- a/.github/autobuild/ios.sh +++ b/.github/autobuild/ios.sh @@ -2,7 +2,7 @@ set -eu QT_DIR=/usr/local/opt/qt -AQTINSTALL_VERSION=2.0.6 +AQTINSTALL_VERSION=2.1.0 if [[ ! ${QT_VERSION:-} =~ [0-9]+\.[0-9]+\..* ]]; then echo "Environment variable QT_VERSION must be set to a valid Qt version" @@ -19,7 +19,14 @@ setup() { else echo "Installing Qt" python3 -m pip install "aqtinstall==${AQTINSTALL_VERSION}" - python3 -m aqt install-qt --outputdir "${QT_DIR}" mac ios "${QT_VERSION}" --archives qtbase qttools qttranslations qtmacextras + # Install actual ios Qt: + python3 -m aqt install-qt --outputdir "${QT_DIR}" mac ios "${QT_VERSION}" --archives qtbase qttools qttranslations + if [[ ! "${QT_VERSION}" =~ 5\..* ]]; then + # Starting with Qt6, ios' qtbase install does no longer include a real qmake binary. + # Instead, it is a script which invokes the mac desktop qmake. + # As of aqtinstall 2.1.0 / 04/2022, desktop qtbase has to be installed manually: + python3 -m aqt install-qt --outputdir "${QT_DIR}" mac desktop "${QT_VERSION}" --archives qtbase + fi fi } diff --git a/.github/autobuild/mac.sh b/.github/autobuild/mac.sh index a38cc393e7..abf9ccc372 100755 --- a/.github/autobuild/mac.sh +++ b/.github/autobuild/mac.sh @@ -19,7 +19,7 @@ setup() { else echo "Installing Qt..." python3 -m pip install "aqtinstall==${AQTINSTALL_VERSION}" - python3 -m aqt install-qt --outputdir "${QT_DIR}" mac desktop "${QT_VERSION}" --archives qtbase qttools qttranslations qtmacextras + python3 -m aqt install-qt --outputdir "${QT_DIR}" mac desktop "${QT_VERSION}" --archives qtbase qttools qttranslations fi } diff --git a/.github/autobuild/windows.ps1 b/.github/autobuild/windows.ps1 index ace3808658..19c9993c8d 100644 --- a/.github/autobuild/windows.ps1 +++ b/.github/autobuild/windows.ps1 @@ -42,7 +42,7 @@ Function Install-Qt "desktop", "$QtVersion", "$QtArch", - "--archives", "qtbase", "qttools", "qttranslations", "qtwinextras" + "--archives", "qtbase", "qttools", "qttranslations" ) aqt install-qt @Args if ( !$? ) diff --git a/Jamulus.pro b/Jamulus.pro index a6d8dbc05c..7d89bbfbdd 100644 --- a/Jamulus.pro +++ b/Jamulus.pro @@ -88,6 +88,14 @@ win32 { advapi32.lib \ winmm.lib \ ws2_32.lib + greaterThan(QT_MAJOR_VERSION, 5) { + # Qt5 had a special qtmain library which took care of forwarding the MSVC default WinMain() entrypoint to + # the platform-agnostic main(). + # Qt6 is still supposed to have that lib under the new name QtEntryPoint. As it does not seem + # to be effective when building with qmake, we are rather instructing MSVC to use the platform-agnostic + # main() entrypoint directly: + QMAKE_LFLAGS += /subsystem:windows /ENTRY:mainCRTStartup + } } contains(CONFIG, "serveronly") { @@ -154,9 +162,8 @@ win32 { RC_FILE = mac/mainicon.icns } - QT += macextras - HEADERS += mac/activity.h - OBJECTIVE_SOURCES += mac/activity.mm + HEADERS += mac/activity.h mac/badgelabel.h + OBJECTIVE_SOURCES += mac/activity.mm mac/badgelabel.mm CONFIG += x86 QMAKE_TARGET_BUNDLE_PREFIX = io.jamulus @@ -185,7 +192,8 @@ win32 { -framework CoreMIDI \ -framework AudioToolbox \ -framework AudioUnit \ - -framework Foundation + -framework Foundation \ + -framework AppKit contains(CONFIG, "jackonmac") { message(Using JACK.) @@ -208,7 +216,6 @@ win32 { } else:ios { QMAKE_INFO_PLIST = ios/Info.plist - QT += macextras OBJECTIVE_SOURCES += ios/ios_app_delegate.mm HEADERS += ios/ios_app_delegate.h HEADERS += ios/sound.h diff --git a/ios/deploy_ios.sh b/ios/deploy_ios.sh index 4344761480..0555fd9e2b 100755 --- a/ios/deploy_ios.sh +++ b/ios/deploy_ios.sh @@ -5,7 +5,7 @@ set -eu # Create Xcode file and build qmake -spec macx-xcode Jamulus.pro -/usr/bin/xcodebuild -project Jamulus.xcodeproj -scheme Jamulus -configuration Release clean archive -archivePath "build/Jamulus.xcarchive" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO +/usr/bin/xcodebuild -project Jamulus.xcodeproj -scheme Jamulus -configuration Release clean archive -archivePath "build/Jamulus.xcarchive" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO CODE_SIGN_ENTITLEMENTS="" # Generate ipa by copying the .app file from the xcarchive directory mkdir build/Payload diff --git a/mac/badgelabel.h b/mac/badgelabel.h new file mode 100644 index 0000000000..31c243d1ed --- /dev/null +++ b/mac/badgelabel.h @@ -0,0 +1,27 @@ +/******************************************************************************\ + * Copyright (c) 2022 + * + * Author(s): + * The Jamulus Development Team + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#include + +void SetMacBadgeLabelText ( const QString& text ); diff --git a/mac/badgelabel.mm b/mac/badgelabel.mm new file mode 100644 index 0000000000..d6d0101643 --- /dev/null +++ b/mac/badgelabel.mm @@ -0,0 +1,29 @@ +/******************************************************************************\ + * Copyright (c) 2022 + * + * Author(s): + * The Jamulus Development Team + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#include "badgelabel.h" +#import +#import + +void SetMacBadgeLabelText ( const QString& text ) { [[[NSApplication sharedApplication] dockTile] setBadgeLabel:text.toNSString()]; } diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index c94c03b707..96a4d8b5a9 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -938,19 +938,16 @@ void CClientDlg::SetMyWindowTitle ( const int iNumClients ) #if defined( Q_OS_MACX ) // for MacOS only we show the number of connected clients as a // badge label text if more than one user is connected - // (only available in Qt5.2) -# if QT_VERSION >= QT_VERSION_CHECK( 5, 2, 0 ) if ( iNumClients > 1 ) { // show the number of connected clients - QtMac::setBadgeLabelText ( QString ( "%1" ).arg ( iNumClients ) ); + SetMacBadgeLabelText ( QString ( "%1" ).arg ( iNumClients ) ); } else { // clear the text (apply an empty string) - QtMac::setBadgeLabelText ( "" ); + SetMacBadgeLabelText ( "" ); } -# endif #endif } diff --git a/src/clientdlg.h b/src/clientdlg.h index bbcc604766..e7e8ac1e8b 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -37,6 +37,7 @@ #include #include #include +#include #if QT_VERSION >= QT_VERSION_CHECK( 5, 6, 0 ) # include #endif @@ -51,10 +52,8 @@ #include "connectdlg.h" #include "analyzerconsole.h" #include "ui_clientdlgbase.h" -#if defined( __APPLE__ ) || defined( __MACOSX ) -# if QT_VERSION >= QT_VERSION_CHECK( 5, 2, 0 ) -# include -# endif +#if defined( Q_OS_MACX ) +# include "mac/badgelabel.h" #endif /* Definitions ****************************************************************/ diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 78f2353b50..8b2b50b851 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -561,21 +561,31 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet for ( int iCurCntry = static_cast ( QLocale::AnyCountry ); iCurCntry < static_cast ( QLocale::LastCountry ); iCurCntry++ ) { // exclude the "None" entry since it is added after the sorting - if ( static_cast ( iCurCntry ) != QLocale::AnyCountry ) + if ( static_cast ( iCurCntry ) == QLocale::AnyCountry ) { - // get current country enum - QLocale::Country eCountry = static_cast ( iCurCntry ); - - // try to load icon from resource file name - QIcon CurFlagIcon; - CurFlagIcon.addFile ( CLocale::GetCountryFlagIconsResourceReference ( eCountry ) ); - - // only add the entry if a flag is available - if ( !CurFlagIcon.isNull() ) - { - // create a combo box item with text and image - pcbxCountry->addItem ( QIcon ( CurFlagIcon ), QLocale::countryToString ( eCountry ), iCurCntry ); - } + continue; + } + + if ( !CLocale::IsCountryCodeSupported ( iCurCntry ) ) + { + // The current Qt version which is the base for the loop may support + // more country codes than our protocol does. Therefore, skip + // the unsupported options to avoid surprises. + continue; + } + + // get current country enum + QLocale::Country eCountry = static_cast ( iCurCntry ); + + // try to load icon from resource file name + QIcon CurFlagIcon; + CurFlagIcon.addFile ( CLocale::GetCountryFlagIconsResourceReference ( eCountry ) ); + + // only add the entry if a flag is available + if ( !CurFlagIcon.isNull() ) + { + // create a combo box item with text and image + pcbxCountry->addItem ( QIcon ( CurFlagIcon ), QLocale::countryToString ( eCountry ), iCurCntry ); } } diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index 60e0804bf7..855c7099d3 100644 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -750,7 +750,12 @@ void CConnectDlg::OnTimerPing() bEnableIPv6 ) ) { // if address is valid, send ping message using a new thread +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + QFuture f = QtConcurrent::run ( &CConnectDlg::EmitCLServerListPingMes, this, haServerAddress ); + Q_UNUSED ( f ); +#else QtConcurrent::run ( this, &CConnectDlg::EmitCLServerListPingMes, haServerAddress ); +#endif } } } diff --git a/src/levelmeter.cpp b/src/levelmeter.cpp index 9eb126fe94..fccafe44d5 100644 --- a/src/levelmeter.cpp +++ b/src/levelmeter.cpp @@ -34,7 +34,7 @@ CLevelMeter::CLevelMeter ( QWidget* parent ) : QWidget ( parent ), eLevelMeterTy QWidget* pLEDMeter = new QWidget(); QVBoxLayout* pLEDLayout = new QVBoxLayout ( pLEDMeter ); pLEDLayout->setAlignment ( Qt::AlignHCenter ); - pLEDLayout->setMargin ( 0 ); + pLEDLayout->setContentsMargins ( 0, 0, 0, 0 ); pLEDLayout->setSpacing ( 0 ); // create LEDs plus the clip LED diff --git a/src/main.cpp b/src/main.cpp index 0bc792376b..4e09bc4f73 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1051,7 +1051,7 @@ QString UsageArguments ( char** argv ) " -L, --licence show an agreement window before users can connect\n" " -m, --htmlstatus enable HTML status file, set file name\n" " -o, --serverinfo registration info for this Server. Format:\n" - " [name];[city];[country as QLocale ID]\n" + " [name];[city];[country as Qt5 QLocale ID]\n" " --serverpublicip public IP address for this Server. Needed when\n" " registering with a server list hosted\n" " behind the same NAT\n" diff --git a/src/protocol.cpp b/src/protocol.cpp index 62504ed15f..f6c518baf4 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -1152,7 +1152,7 @@ void CProtocol::CreateConClientListMes ( const CVector& vecChanInf PutValOnStream ( vecData, iPos, static_cast ( vecChanInfo[i].iChanID ), 1 ); // country (2 bytes) - PutValOnStream ( vecData, iPos, static_cast ( vecChanInfo[i].eCountry ), 2 ); + PutCountryOnStream ( vecData, iPos, vecChanInfo[i].eCountry ); // instrument (4 bytes) PutValOnStream ( vecData, iPos, static_cast ( vecChanInfo[i].iInstrument ), 4 ); @@ -1191,7 +1191,7 @@ bool CProtocol::EvaluateConClientListMes ( const CVector& vecData ) const int iChanID = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); // country (2 bytes) - const QLocale::Country eCountry = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + const QLocale::Country eCountry = GetCountryFromStream ( vecData, iPos ); // instrument (4 bytes) const int iInstrument = static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); @@ -1261,7 +1261,7 @@ void CProtocol::CreateChanInfoMes ( const CChannelCoreInfo ChanInfo ) CVector vecData ( iEntrLen ); // country (2 bytes) - PutValOnStream ( vecData, iPos, static_cast ( ChanInfo.eCountry ), 2 ); + PutCountryOnStream ( vecData, iPos, ChanInfo.eCountry ); // instrument (4 bytes) PutValOnStream ( vecData, iPos, static_cast ( ChanInfo.iInstrument ), 4 ); @@ -1291,7 +1291,7 @@ bool CProtocol::EvaluateChanInfoMes ( const CVector& vecData ) } // country (2 bytes) - ChanInfo.eCountry = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + ChanInfo.eCountry = GetCountryFromStream ( vecData, iPos ); // instrument (4 bytes) ChanInfo.iInstrument = static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); @@ -1773,7 +1773,7 @@ void CProtocol::CreateCLRegisterServerMes ( const CHostAddress& InetAddr, const PutValOnStream ( vecData, iPos, static_cast ( LInetAddr.iPort ), 2 ); // country (2 bytes) - PutValOnStream ( vecData, iPos, static_cast ( ServerInfo.eCountry ), 2 ); + PutCountryOnStream ( vecData, iPos, ServerInfo.eCountry ); // maximum number of connected clients (1 byte) PutValOnStream ( vecData, iPos, static_cast ( ServerInfo.iMaxNumClients ), 1 ); @@ -1811,7 +1811,7 @@ bool CProtocol::EvaluateCLRegisterServerMes ( const CHostAddress& InetAddr, cons LInetAddr.iPort = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); // country (2 bytes) - RecServerInfo.eCountry = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + RecServerInfo.eCountry = GetCountryFromStream ( vecData, iPos ); // maximum number of connected clients (1 byte) RecServerInfo.iMaxNumClients = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); @@ -1887,7 +1887,7 @@ void CProtocol::CreateCLRegisterServerExMes ( const CHostAddress& InetAddr, cons PutValOnStream ( vecData, iPos, static_cast ( LInetAddr.iPort ), 2 ); // country (2 bytes) - PutValOnStream ( vecData, iPos, static_cast ( ServerInfo.eCountry ), 2 ); + PutCountryOnStream ( vecData, iPos, ServerInfo.eCountry ); // maximum number of connected clients (1 byte) PutValOnStream ( vecData, iPos, static_cast ( ServerInfo.iMaxNumClients ), 1 ); @@ -1931,7 +1931,7 @@ bool CProtocol::EvaluateCLRegisterServerExMes ( const CHostAddress& InetAddr, co LInetAddr.iPort = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); // country (2 bytes) - RecServerInfo.eCountry = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + RecServerInfo.eCountry = GetCountryFromStream ( vecData, iPos ); // maximum number of connected clients (1 byte) RecServerInfo.iMaxNumClients = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); @@ -2045,7 +2045,7 @@ void CProtocol::CreateCLServerListMes ( const CHostAddress& InetAddr, const CVec PutValOnStream ( vecData, iPos, static_cast ( vecServerInfo[i].HostAddr.iPort ), 2 ); // country (2 bytes) - PutValOnStream ( vecData, iPos, static_cast ( vecServerInfo[i].eCountry ), 2 ); + PutCountryOnStream ( vecData, iPos, vecServerInfo[i].eCountry ); // maximum number of connected clients (1 byte) PutValOnStream ( vecData, iPos, static_cast ( vecServerInfo[i].iMaxNumClients ), 1 ); @@ -2087,7 +2087,7 @@ bool CProtocol::EvaluateCLServerListMes ( const CHostAddress& InetAddr, const CV const quint16 iPort = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); // country (2 bytes) - const QLocale::Country eCountry = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + const QLocale::Country eCountry = GetCountryFromStream ( vecData, iPos ); // maximum number of connected clients (1 byte) const int iMaxNumClients = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); @@ -2396,7 +2396,7 @@ void CProtocol::CreateCLConnClientsListMes ( const CHostAddress& InetAddr, const PutValOnStream ( vecData, iPos, static_cast ( vecChanInfo[i].iChanID ), 1 ); // country (2 bytes) - PutValOnStream ( vecData, iPos, static_cast ( vecChanInfo[i].eCountry ), 2 ); + PutCountryOnStream ( vecData, iPos, vecChanInfo[i].eCountry ); // instrument (4 bytes) PutValOnStream ( vecData, iPos, static_cast ( vecChanInfo[i].iInstrument ), 4 ); @@ -2435,7 +2435,7 @@ bool CProtocol::EvaluateCLConnClientsListMes ( const CHostAddress& InetAddr, con const int iChanID = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); // country (2 bytes) - const QLocale::Country eCountry = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + const QLocale::Country eCountry = GetCountryFromStream ( vecData, iPos ); // instrument (4 bytes) const int iInstrument = static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); @@ -2769,6 +2769,12 @@ bool CProtocol::GetStringFromStream ( const CVector& vecIn, int& iPos, return false; // no error } +QLocale::Country CProtocol::GetCountryFromStream ( const CVector& vecIn, int& iPos ) +{ + unsigned short iCountryCode = GetValFromStream ( vecIn, iPos, 2 ); + return CLocale::WireFormatCountryCodeToQtCountry ( iCountryCode ); +} + void CProtocol::GenMessageFrame ( CVector& vecOut, const int iCnt, const int iID, const CVector& vecData ) { int i; @@ -2881,3 +2887,9 @@ void CProtocol::PutStringUTF8OnStream ( CVector& vecIn, int& iPos, cons PutValOnStream ( vecIn, iPos, static_cast ( sStringUTF8[j] ), 1 ); } } + +void CProtocol::PutCountryOnStream ( CVector& vecIn, int& iPos, QLocale::Country eCountry ) +{ + unsigned short iCountryCode = CLocale::QtCountryToWireFormatCountryCode ( eCountry ); + PutValOnStream ( vecIn, iPos, iCountryCode, 2 ); +} diff --git a/src/protocol.h b/src/protocol.h index 334ce0af92..e05db57611 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -217,6 +217,8 @@ void CreateReqChannelLevelListMes(); const QByteArray& sStringUTF8, const int iNumberOfBytsLen = 2 ); // default is 2 bytes length indicator + void PutCountryOnStream ( CVector& vecIn, int& iPos, QLocale::Country eCountry ); + static uint32_t GetValFromStream ( const CVector& vecIn, int& iPos, const int iNumOfBytes ); bool GetStringFromStream ( const CVector& vecIn, @@ -225,6 +227,8 @@ void CreateReqChannelLevelListMes(); QString& strOut, const int iNumberOfBytsLen = 2 ); // default is 2 bytes length indicator + static QLocale::Country GetCountryFromStream ( const CVector& vecIn, int& iPos ); + void SendMessage(); void CreateAndSendMessage ( const int iID, const CVector& vecData ); diff --git a/src/recorder/creaperproject.cpp b/src/recorder/creaperproject.cpp index 6c1213dd68..20c722131c 100644 --- a/src/recorder/creaperproject.cpp +++ b/src/recorder/creaperproject.cpp @@ -62,21 +62,21 @@ CReaperItem::CReaperItem ( const QString& name, const STrackItem& trackItem, con QTextStream sOut ( &out ); - sOut << " " << endl; - - sOut << " >"; + sOut << " " << '\n' + + << " >"; sOut.flush(); } @@ -91,14 +91,14 @@ CReaperTrack::CReaperTrack ( QString name, qint32& iid, QList items, { QTextStream sOut ( &out ); - sOut << " > tracks, int fr { QTextStream sOut ( &out ); - sOut << ""; diff --git a/src/recorder/cwavestream.h b/src/recorder/cwavestream.h index 708304b8dd..d10e72885f 100644 --- a/src/recorder/cwavestream.h +++ b/src/recorder/cwavestream.h @@ -25,6 +25,8 @@ #pragma once #include +#include +#include namespace recorder { diff --git a/src/recorder/jamrecorder.cpp b/src/recorder/jamrecorder.cpp index 95294ae428..930f21b353 100644 --- a/src/recorder/jamrecorder.cpp +++ b/src/recorder/jamrecorder.cpp @@ -479,7 +479,7 @@ void CJamRecorder::ReaperProjectFromCurrentSession() if ( outf.open ( QFile::WriteOnly ) ) { QTextStream out ( &outf ); - out << CReaperProject ( currentSession->Tracks(), iServerFrameSizeSamples ).toString() << endl; + out << CReaperProject ( currentSession->Tracks(), iServerFrameSizeSamples ).toString() << '\n'; qDebug() << "Session RPP:" << reaperProjectFileName; } else @@ -511,7 +511,7 @@ void CJamRecorder::AudacityLofFromCurrentSession() { QFileInfo fi ( item.fileName ); sOut << "file " << '"' << fi.fileName() << '"'; - sOut << " offset " << secondsAt48K ( item.startFrame, iServerFrameSizeSamples ) << endl; + sOut << " offset " << secondsAt48K ( item.startFrame, iServerFrameSizeSamples ) << '\n'; } } @@ -556,7 +556,7 @@ void CJamRecorder::SessionDirToReaper ( QString& strSessionDirName, int serverFr out << CReaperProject ( CJamSession::TracksFromSessionDir ( fiSessionDir.absoluteFilePath(), serverFrameSizeSamples ), serverFrameSizeSamples ) .toString() - << endl; + << '\n'; qDebug() << "Session RPP:" << reaperProjectFileName; } diff --git a/src/server.h b/src/server.h index a90c64b47e..c76aa99147 100644 --- a/src/server.h +++ b/src/server.h @@ -29,7 +29,6 @@ #include #include #include -#include #include #ifdef USE_OPUS_SHARED_LIB # include "opus/opus_custom.h" diff --git a/src/serverdlg.cpp b/src/serverdlg.cpp index f467a2f22b..704343f793 100644 --- a/src/serverdlg.cpp +++ b/src/serverdlg.cpp @@ -308,12 +308,22 @@ lvwClients->setMinimumHeight ( 140 ); for ( int iCurCntry = static_cast ( QLocale::AnyCountry ); iCurCntry < static_cast ( QLocale::LastCountry ); iCurCntry++ ) { // add all countries except of the "Default" country - if ( static_cast ( iCurCntry ) != QLocale::AnyCountry ) + if ( static_cast ( iCurCntry ) == QLocale::AnyCountry ) { - // store the country enum index together with the string (this is - // important since we sort the combo box items later on) - cbxLocationCountry->addItem ( QLocale::countryToString ( static_cast ( iCurCntry ) ), iCurCntry ); + continue; } + + if ( !CLocale::IsCountryCodeSupported ( iCurCntry ) ) + { + // The current Qt version which is the base for the loop may support + // more country codes than our protocol does. Therefore, skip + // the unsupported options to avoid surprises. + continue; + } + + // store the country enum index together with the string (this is + // important since we sort the combo box items later on) + cbxLocationCountry->addItem ( QLocale::countryToString ( static_cast ( iCurCntry ) ), iCurCntry ); } // sort country combo box items in alphabetical order diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 3f93bc6943..b3c2987688 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -771,7 +771,7 @@ void CServerListManager::Save() .arg ( ServerList[iIdx].HostAddr.toString() ) .arg ( ServerList[iIdx].LHostAddr.toString() ) .arg ( ServerList[iIdx].strName ) ); - out << ServerList[iIdx].toCSV() << "\n"; + out << ServerList[iIdx].toCSV() << '\n'; } } diff --git a/src/serverlogging.cpp b/src/serverlogging.cpp index 8d5732bf42..c88f951c79 100644 --- a/src/serverlogging.cpp +++ b/src/serverlogging.cpp @@ -70,8 +70,8 @@ void CServerLogging::operator<< ( const QString& sNewStr ) { // append new line in logging file QTextStream out ( &File ); - out << sNewStr << endl; - File.flush(); + out << sNewStr << '\n'; + out.flush(); } } diff --git a/src/settings.cpp b/src/settings.cpp index ab9b038dfd..1fb14d115e 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -296,7 +296,7 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, // country if ( GetNumericIniSet ( IniXMLDocument, "client", "country", 0, static_cast ( QLocale::LastCountry ), iValue ) ) { - pClient->ChannelInfo.eCountry = static_cast ( iValue ); + pClient->ChannelInfo.eCountry = CLocale::WireFormatCountryCodeToQtCountry ( iValue ); } else { @@ -636,7 +636,7 @@ void CClientSettings::WriteSettingsToXML ( QDomDocument& IniXMLDocument ) SetNumericIniSet ( IniXMLDocument, "client", "instrument", pClient->ChannelInfo.iInstrument ); // country - SetNumericIniSet ( IniXMLDocument, "client", "country", static_cast ( pClient->ChannelInfo.eCountry ) ); + SetNumericIniSet ( IniXMLDocument, "client", "country", CLocale::QtCountryToWireFormatCountryCode ( pClient->ChannelInfo.eCountry ) ); // city PutIniSetting ( IniXMLDocument, "client", "city_base64", ToBase64 ( pClient->ChannelInfo.strCity ) ); @@ -784,7 +784,7 @@ void CServerSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, // country if ( GetNumericIniSet ( IniXMLDocument, "server", "country", 0, static_cast ( QLocale::LastCountry ), iValue ) ) { - pServer->SetServerCountry ( static_cast ( iValue ) ); + pServer->SetServerCountry ( CLocale::WireFormatCountryCodeToQtCountry ( iValue ) ); } } @@ -921,7 +921,7 @@ void CServerSettings::WriteSettingsToXML ( QDomDocument& IniXMLDocument ) PutIniSetting ( IniXMLDocument, "server", "city", pServer->GetServerCity() ); // country - SetNumericIniSet ( IniXMLDocument, "server", "country", static_cast ( pServer->GetServerCountry() ) ); + SetNumericIniSet ( IniXMLDocument, "server", "country", CLocale::QtCountryToWireFormatCountryCode ( pServer->GetServerCountry() ) ); // norecord flag SetFlagIniSet ( IniXMLDocument, "server", "norecord", pServer->GetDisableRecording() ); diff --git a/src/util.cpp b/src/util.cpp index d2f7d6f78b..4f7d6e1122 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1202,7 +1202,42 @@ CInstPictures::EInstCategory CInstPictures::GetCategory ( const int iInstrument } // Locale management class ----------------------------------------------------- -QString CLocale::GetCountryFlagIconsResourceReference ( const QLocale::Country eCountry ) +QLocale::Country CLocale::WireFormatCountryCodeToQtCountry ( unsigned short iCountryCode ) +{ +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + // The Jamulus protocol wire format gives us Qt5 country IDs. + // Qt6 changed those IDs, so we have to convert back: + return (QLocale::Country) wireFormatToQt6Table[iCountryCode]; +#else + return (QLocale::Country) iCountryCode; +#endif +} + +unsigned short CLocale::QtCountryToWireFormatCountryCode ( const QLocale::Country eCountry ) +{ +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + // The Jamulus protocol wire format expects Qt5 country IDs. + // Qt6 changed those IDs, so we have to convert back: + return qt6CountryToWireFormat[(unsigned short) eCountry]; +#else + return (unsigned short) eCountry; +#endif +} + +bool CLocale::IsCountryCodeSupported ( unsigned short iCountryCode ) +{ +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + // On newer Qt versions there might be codes which do not have a Qt5 equivalent. + // We have no way to support those sanely right now. + return qt6CountryToWireFormat[iCountryCode] != -1; +#else + // All Qt5 codes are supported. + Q_UNUSED ( iCountryCode ); + return true; +#endif +} + +QString CLocale::GetCountryFlagIconsResourceReference ( const QLocale::Country eCountry /* Qt-native value */ ) { QString strReturn = ""; diff --git a/src/util.h b/src/util.h index 4a546781c9..49a8bca0bd 100644 --- a/src/util.h +++ b/src/util.h @@ -648,7 +648,7 @@ inline QString svrRegStatusToString ( ESvrRegStatus eSvrRegStatus ) return QCoreApplication::translate ( "CServerDlg", "Requirements not fulfilled" ); } - return QString ( QCoreApplication::translate ( "CServerDlg", "Unknown value " ) ).append ( eSvrRegStatus ); + return QString ( QCoreApplication::translate ( "CServerDlg", "Unknown value %1" ) ).arg ( eSvrRegStatus ); } // Directory server registration outcome --------------------------------------- @@ -813,10 +813,40 @@ class CInstPictures class CLocale { public: - static QString GetCountryFlagIconsResourceReference ( const QLocale::Country eCountry ); + static QString GetCountryFlagIconsResourceReference ( const QLocale::Country eCountry /* Always a Qt5 (!) code */ ); static QMap GetAvailableTranslations(); static QPair FindSysLangTransFileName ( const QMap& TranslMap ); static void LoadTranslation ( const QString strLanguage, QCoreApplication* pApp ); + static QLocale::Country WireFormatCountryCodeToQtCountry ( unsigned short iCountryCode ); + static unsigned short QtCountryToWireFormatCountryCode ( const QLocale::Country eCountry ); + static bool IsCountryCodeSupported ( unsigned short iCountryCode ); +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + // ./tools/qt5-to-qt6-country-code-table.py generates these lists: + constexpr int const static wireFormatToQt6Table[] = { + 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 35, 36, 37, 38, 39, 40, 41, 43, 45, 46, 48, 49, 50, 51, 53, 54, 55, 57, 56, 58, 59, 118, + 60, 61, 63, 64, 65, 67, 68, 69, 232, 70, 71, 72, 73, 74, 75, 77, 80, 81, 82, 83, 84, 100, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 102, 101, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 116, 117, 119, + 120, 122, 123, 124, 125, 174, 218, 127, 128, 129, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 161, 162, 163, 164, 165, 62, 166, 167, 168, 170, 169, 171, 172, 173, 175, + 176, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 197, 198, 201, 202, 203, 204, 205, 206, 208, + 209, 210, 212, 213, 214, 215, 216, 217, 220, 221, 196, 200, 222, 223, 224, 76, 225, 226, 227, 228, 229, 230, 231, 233, 234, 235, 236, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 248, 247, 250, 251, 252, 253, 254, 255, 34, 249, 256, 257, 259, 42, 260, 261, 52, 157, + 207, 195, 199, 130, 14, 2, 66, 47, 115, 121, 237, 219, 44, 211, 126, 79, 177, 258, 78, + }; + constexpr int const static qt6CountryToWireFormat[] = { + 0, 1, 248, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 247, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 233, 32, 33, 34, 35, 36, 37, 38, 238, 39, 255, 40, 41, 250, 42, 43, 44, 45, 241, 46, + 47, 48, 50, 49, 51, 52, 54, 55, 152, 56, 57, 58, 249, 59, 60, 61, 63, 64, 65, 66, 67, 68, 204, 69, 261, 258, 70, + 71, 72, 73, 74, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 75, 92, 91, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 251, 105, 106, 53, 107, 108, 252, 109, 110, 111, 112, 257, 115, 116, 117, 246, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 242, 144, 145, 146, 147, + 148, 149, 150, 151, 153, 154, 155, 157, 156, 158, 159, 160, 113, 161, 162, 259, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 244, 199, 180, 181, 245, 200, 182, 183, 184, 185, 186, 187, 243, 188, 189, 190, 256, 191, 192, 193, 194, + 195, 196, 114, 254, 197, 198, 201, 202, 203, 205, 206, 207, 208, 209, 210, 211, 62, 212, 213, 214, 215, 253, 216, 217, 218, 219, 220, + 221, 222, 223, 224, 226, 225, 234, 227, 228, 229, 230, 231, 232, 235, 236, 260, 237, 239, 240, + }; +#endif }; // Info of a channel ----------------------------------------------------------- @@ -1213,7 +1243,7 @@ class CTimingMeas for ( int i = 0; i < iNumMeas; i++ ) { // convert ns in ms and store the value - streamFile << i << " " << static_cast ( vElapsedTimes[i] ) / 1000000 << endl; + streamFile << i << " " << static_cast ( vElapsedTimes[i] ) / 1000000 << "\n"; } } } diff --git a/tools/qt5-to-qt6-country-code-table.py b/tools/qt5-to-qt6-country-code-table.py new file mode 100755 index 0000000000..4d54bf9a01 --- /dev/null +++ b/tools/qt5-to-qt6-country-code-table.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +""" +Jamulus uses Qt5 QLocale::Country codes in its protocol. +Qt6 changed those codes in an incompatible way. +To make >=Qt6 clients compatible with the Jamulus protocol in the wild, +we need to translate between those codes. +This script generates two translation tables from Qt5 + Qt6 header files: +A table to translate from Qt5 to Qt6 and an inverse table. +""" +import re + +# verify that the following actually points to Qt5 on your system! +qt5_include = '/usr/include/qt/QtCore/qlocale.h' +qt6_include = '/usr/include/qt6/QtCore/qlocale.h' + + +def parse_enum_from_header(header): + name_to_val = {} + val_to_name = {} + with open(header) as f: + s = f.read() + matches = re.search(r'enum Country[^\n]+\{([^}]+)\}', s) + assignments = matches.group(1) + + assign_pattern = re.compile(r'^\s*(\S+)(?:\s+.*)?\s+=\s+([^\s,]+),?$') + for assignment in assignments.split('\n'): + if assignment.startswith('#') or not assignment.strip(): + continue + matches = assign_pattern.match(assignment) + if not matches: + raise Exception("unable to match line: %r" % assignment) + name = matches.group(1) + val = matches.group(2) + if val.isnumeric(): + val = int(val) + if val not in val_to_name: + val_to_name[val] = name + name_to_val[name] = val + elif val in name_to_val: + # resolve aliases: + name_to_val[name] = name_to_val[val] + else: + raise("Unhandled case") + return name_to_val, val_to_name + + +def make_struct(name, table): + ret = '' + highest_key = max([int(x) for x in table.keys()]) + ret += 'constexpr int const static %s[] = {' % (name,) + for key in range(highest_key+1): + ret += '%d' % table.get(key, -1) # fill gaps with -1 + ret += ', ' + ret += '};' + return ret + + +qt5_name_to_val, qt5_val_to_name = parse_enum_from_header(qt5_include) +qt6_name_to_val, qt6_val_to_name = parse_enum_from_header(qt6_include) + +qt5_to_qt6 = {} +qt6_to_qt5 = {} +# We need to support all Qt5 codes, so we work by value: +for qt5_val, name in qt5_val_to_name.items(): + qt6_val = qt6_name_to_val[name] + qt5_to_qt6[qt5_val] = qt6_val + qt6_to_qt5[qt6_val] = qt5_val + +print(make_struct('wireFormatToQt6Table', qt5_to_qt6)) +print(make_struct('qt6CountryToWireFormat', qt6_to_qt5))