Compare commits

..

1 Commits

Author SHA1 Message Date
joshua
98e265da72 Test font size in pixel for DynamicElementTextItem 2021-12-31 18:07:22 +01:00
4535 changed files with 74596 additions and 137521 deletions

View File

@@ -12,9 +12,10 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: snapcore/action-build@v1 - uses: snapcore/action-build@v1
id: build id: build
- uses: snapcore/action-publish@v1
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.STORE_LOGIN }}
with: with:
snapcraft-args: --enable-experimental-extensions
- uses: snapcore/action-publish@v1
with:
store_login: ${{ secrets.STORE_LOGIN }}
snap: ${{ steps.build.outputs.snap }} snap: ${{ steps.build.outputs.snap }}
release: edge release: edge

View File

@@ -3,31 +3,6 @@ Changelog
If by accident I have forgotten to credit someone in the CHANGELOG, email me and I will fix it. If by accident I have forgotten to credit someone in the CHANGELOG, email me and I will fix it.
__3.3.4__
---------
* Fix compilation under Qt 6.2+ and stricter Qt compile settings. - _Christoph Cullmann_
__3.3.3__
---------
* Support for Qt 6.3+ - Fixed deprecated `QCryptographicHash::addData()` that will only support `QByteArrayView` going further. - _Moody Liu_
__3.3.2__
---------
* Fixed crash caused by sending a `writeAck` on a removed connection. - _Nicolas Werner_
__3.3.1__
---------
* Added support for _AppImage_ dynamic executable paths. - _Michael Klein_
__3.3.0__
---------
* Fixed message fragmentation issue causing crashes and incorrectly / inconsistently received messages. - _Nils Jeisecke_
__3.2.0__ __3.2.0__
--------- ---------

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.12.0) cmake_minimum_required(VERSION 3.7.0)
project(SingleApplication LANGUAGES CXX) project(SingleApplication LANGUAGES CXX)
@@ -38,13 +38,3 @@ endif()
target_compile_definitions(${PROJECT_NAME} PUBLIC QAPPLICATION_CLASS=${QAPPLICATION_CLASS}) target_compile_definitions(${PROJECT_NAME} PUBLIC QAPPLICATION_CLASS=${QAPPLICATION_CLASS})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(${PROJECT_NAME} PRIVATE
QT_NO_CAST_TO_ASCII
QT_NO_CAST_FROM_ASCII
QT_NO_URL_CAST_FROM_STRING
QT_NO_CAST_FROM_BYTEARRAY
QT_USE_QSTRINGBUILDER
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
QT_NO_KEYWORDS
QT_NO_FOREACH
)

View File

@@ -35,7 +35,7 @@ To include the library files I would recommend that you add it as a git
submodule to your project. Here is how: submodule to your project. Here is how:
```bash ```bash
git submodule add https://github.com/itay-grudev/SingleApplication.git singleapplication git submodule add git@github.com:itay-grudev/SingleApplication.git singleapplication
``` ```
**Qmake:** **Qmake:**
@@ -182,8 +182,7 @@ bool SingleApplication::sendMessage( QByteArray message, int timeout = 100 )
``` ```
Sends `message` to the Primary Instance. Uses `timeout` as a the maximum timeout Sends `message` to the Primary Instance. Uses `timeout` as a the maximum timeout
in milliseconds for blocking functions. Returns `true` if the message has been sent in milliseconds for blocking functions
successfully. If the message can't be sent or the function timeouts - returns `false`.
--- ---

View File

@@ -2,6 +2,7 @@
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// Allow secondary instances
SingleApplication app( argc, argv ); SingleApplication app( argc, argv );
qWarning() << "Started a new instance"; qWarning() << "Started a new instance";

View File

@@ -248,7 +248,10 @@ bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) ) if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) )
return false; return false;
return d->writeConfirmedMessage( timeout, message ); d->socket->write( message );
bool dataWritten = d->socket->waitForBytesWritten( timeout );
d->socket->flush();
return dataWritten;
} }
/** /**

View File

@@ -131,34 +131,21 @@ QString SingleApplicationPrivate::getUsername()
void SingleApplicationPrivate::genBlockServerName() void SingleApplicationPrivate::genBlockServerName()
{ {
QCryptographicHash appData( QCryptographicHash::Sha256 ); QCryptographicHash appData( QCryptographicHash::Sha256 );
#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
appData.addData( "SingleApplication", 17 ); appData.addData( "SingleApplication", 17 );
#else
appData.addData( QByteArrayView{"SingleApplication"} );
#endif
appData.addData( SingleApplication::app_t::applicationName().toUtf8() ); appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
appData.addData( SingleApplication::app_t::organizationName().toUtf8() ); appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() ); appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
if ( ! appDataList.isEmpty() ) if ( ! appDataList.isEmpty() )
appData.addData( appDataList.join(QString()).toUtf8() ); appData.addData( appDataList.join( "" ).toUtf8() );
if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ){ if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ){
appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() ); appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
} }
if( ! (options & SingleApplication::Mode::ExcludeAppPath) ){ if( ! (options & SingleApplication::Mode::ExcludeAppPath) ){
#if defined(Q_OS_WIN) #ifdef Q_OS_WIN
appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() ); appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
#elif defined(Q_OS_LINUX)
// If the application is running as an AppImage then the APPIMAGE env var should be used
// instead of applicationPath() as each instance is launched with its own executable path
const QByteArray appImagePath = qgetenv( "APPIMAGE" );
if( appImagePath.isEmpty() ){ // Not running as AppImage: use path to executable file
appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
} else { // Running as AppImage: Use absolute path to AppImage file
appData.addData( appImagePath );
};
#else #else
appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() ); appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
#endif #endif
@@ -171,7 +158,7 @@ void SingleApplicationPrivate::genBlockServerName()
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
// server naming requirements. // server naming requirements.
blockServerName = QString::fromUtf8(appData.result().toBase64().replace("/", "_")); blockServerName = appData.result().toBase64().replace("/", "_");
} }
void SingleApplicationPrivate::initializeMemoryBlock() const void SingleApplicationPrivate::initializeMemoryBlock() const
@@ -270,52 +257,26 @@ bool SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType conne
writeStream << static_cast<quint8>(connectionType); writeStream << static_cast<quint8>(connectionType);
writeStream << instanceNumber; writeStream << instanceNumber;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(initMsg.constData(), static_cast<quint32>(initMsg.length()))); quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length())));
#else #else
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length())); quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
#endif #endif
writeStream << checksum; writeStream << checksum;
return writeConfirmedMessage( static_cast<int>(msecs - time.elapsed()), initMsg ); // The header indicates the message length that follows
}
void SingleApplicationPrivate::writeAck( QLocalSocket *sock ) {
sock->putChar('\n');
}
bool SingleApplicationPrivate::writeConfirmedMessage (int msecs, const QByteArray &msg)
{
QElapsedTimer time;
time.start();
// Frame 1: The header indicates the message length that follows
QByteArray header; QByteArray header;
QDataStream headerStream(&header, QIODevice::WriteOnly); QDataStream headerStream(&header, QIODevice::WriteOnly);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
headerStream.setVersion(QDataStream::Qt_5_6); headerStream.setVersion(QDataStream::Qt_5_6);
#endif #endif
headerStream << static_cast <quint64>( msg.length() ); headerStream << static_cast <quint64>( initMsg.length() );
if( ! writeConfirmedFrame( static_cast<int>(msecs - time.elapsed()), header )) socket->write( header );
return false; socket->write( initMsg );
bool result = socket->waitForBytesWritten( static_cast<int>(msecs - time.elapsed()) );
// Frame 2: The message
return writeConfirmedFrame( static_cast<int>(msecs - time.elapsed()), msg );
}
bool SingleApplicationPrivate::writeConfirmedFrame( int msecs, const QByteArray &msg )
{
socket->write( msg );
socket->flush(); socket->flush();
return result;
bool result = socket->waitForReadyRead( msecs ); // await ack byte
if (result) {
socket->read( 1 );
return true;
}
return false;
} }
quint16 SingleApplicationPrivate::blockChecksum() const quint16 SingleApplicationPrivate::blockChecksum() const
@@ -360,36 +321,33 @@ void SingleApplicationPrivate::slotConnectionEstablished()
QLocalSocket *nextConnSocket = server->nextPendingConnection(); QLocalSocket *nextConnSocket = server->nextPendingConnection();
connectionMap.insert(nextConnSocket, ConnectionInfo()); connectionMap.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
[nextConnSocket, this](){ [nextConnSocket, this](){
auto &info = connectionMap[nextConnSocket]; auto &info = connectionMap[nextConnSocket];
this->slotClientConnectionClosed( nextConnSocket, info.instanceId ); Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
} }
); );
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater); QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater);
QObject::connect(nextConnSocket, &QLocalSocket::destroyed, this, QObject::connect(nextConnSocket, &QLocalSocket::destroyed,
[nextConnSocket, this](){ [nextConnSocket, this](){
connectionMap.remove(nextConnSocket); connectionMap.remove(nextConnSocket);
} }
); );
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
[nextConnSocket, this](){ [nextConnSocket, this](){
auto &info = connectionMap[nextConnSocket]; auto &info = connectionMap[nextConnSocket];
switch(info.stage){ switch(info.stage){
case StageInitHeader: case StageHeader:
readMessageHeader( nextConnSocket, StageInitBody ); readInitMessageHeader(nextConnSocket);
break; break;
case StageInitBody: case StageBody:
readInitMessageBody(nextConnSocket); readInitMessageBody(nextConnSocket);
break; break;
case StageConnectedHeader: case StageConnected:
readMessageHeader( nextConnSocket, StageConnectedBody ); Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
break;
case StageConnectedBody:
this->slotDataAvailable( nextConnSocket, info.instanceId );
break; break;
default: default:
break; break;
@@ -398,7 +356,7 @@ void SingleApplicationPrivate::slotConnectionEstablished()
); );
} }
void SingleApplicationPrivate::readMessageHeader( QLocalSocket *sock, SingleApplicationPrivate::ConnectionStage nextStage ) void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
{ {
if (!connectionMap.contains( sock )){ if (!connectionMap.contains( sock )){
return; return;
@@ -418,35 +376,29 @@ void SingleApplicationPrivate::readMessageHeader( QLocalSocket *sock, SingleAppl
quint64 msgLen = 0; quint64 msgLen = 0;
headerStream >> msgLen; headerStream >> msgLen;
ConnectionInfo &info = connectionMap[sock]; ConnectionInfo &info = connectionMap[sock];
info.stage = nextStage; info.stage = StageBody;
info.msgLen = msgLen; info.msgLen = msgLen;
writeAck( sock ); if ( sock->bytesAvailable() >= (qint64) msgLen ){
} readInitMessageBody( sock );
bool SingleApplicationPrivate::isFrameComplete( QLocalSocket *sock )
{
if (!connectionMap.contains( sock )){
return false;
} }
ConnectionInfo &info = connectionMap[sock];
if( sock->bytesAvailable() < ( qint64 )info.msgLen ){
return false;
}
return true;
} }
void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock ) void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
{ {
Q_Q(SingleApplication); Q_Q(SingleApplication);
if( !isFrameComplete( sock ) ) if (!connectionMap.contains( sock )){
return; return;
}
ConnectionInfo &info = connectionMap[sock];
if( sock->bytesAvailable() < ( qint64 )info.msgLen ){
return;
}
// Read the message body // Read the message body
QByteArray msgBytes = sock->readAll(); QByteArray msgBytes = sock->read(info.msgLen);
QDataStream readStream(msgBytes); QDataStream readStream(msgBytes);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
@@ -472,7 +424,7 @@ void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
readStream >> msgChecksum; readStream >> msgChecksum;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)))); const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
#else #else
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16))); const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
#endif #endif
@@ -486,9 +438,8 @@ void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
return; return;
} }
ConnectionInfo &info = connectionMap[sock];
info.instanceId = instanceId; info.instanceId = instanceId;
info.stage = StageConnectedHeader; info.stage = StageConnected;
if( connectionType == NewInstance || if( connectionType == NewInstance ||
( connectionType == SecondaryInstance && ( connectionType == SecondaryInstance &&
@@ -497,30 +448,21 @@ void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
Q_EMIT q->instanceStarted(); Q_EMIT q->instanceStarted();
} }
writeAck( sock ); if (sock->bytesAvailable() > 0){
Q_EMIT this->slotDataAvailable( sock, instanceId );
}
} }
void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId ) void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
{ {
Q_Q(SingleApplication); Q_Q(SingleApplication);
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
if ( !isFrameComplete( dataSocket ) )
return;
const QByteArray message = dataSocket->readAll();
writeAck( dataSocket );
ConnectionInfo &info = connectionMap[dataSocket];
info.stage = StageConnectedHeader;
Q_EMIT q->receivedMessage( instanceId, message);
} }
void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId ) void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
{ {
if( closedSocket->bytesAvailable() > 0 ) if( closedSocket->bytesAvailable() > 0 )
slotDataAvailable( closedSocket, instanceId ); Q_EMIT slotDataAvailable( closedSocket, instanceId );
} }
void SingleApplicationPrivate::randomSleep() void SingleApplicationPrivate::randomSleep()
@@ -529,7 +471,7 @@ void SingleApplicationPrivate::randomSleep()
QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u )); QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u ));
#else #else
qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() ); qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
QThread::msleep( qrand() % 11 + 8); QThread::msleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 ));
#endif #endif
} }

View File

@@ -61,10 +61,9 @@ public:
Reconnect = 3 Reconnect = 3
}; };
enum ConnectionStage : quint8 { enum ConnectionStage : quint8 {
StageInitHeader = 0, StageHeader = 0,
StageInitBody = 1, StageBody = 1,
StageConnectedHeader = 2, StageConnected = 2,
StageConnectedBody = 3,
}; };
Q_DECLARE_PUBLIC(SingleApplication) Q_DECLARE_PUBLIC(SingleApplication)
@@ -80,12 +79,8 @@ public:
quint16 blockChecksum() const; quint16 blockChecksum() const;
qint64 primaryPid() const; qint64 primaryPid() const;
QString primaryUser() const; QString primaryUser() const;
bool isFrameComplete(QLocalSocket *sock); void readInitMessageHeader(QLocalSocket *socket);
void readMessageHeader(QLocalSocket *socket, ConnectionStage nextStage);
void readInitMessageBody(QLocalSocket *socket); void readInitMessageBody(QLocalSocket *socket);
void writeAck(QLocalSocket *sock);
bool writeConfirmedFrame(int msecs, const QByteArray &msg);
bool writeConfirmedMessage(int msecs, const QByteArray &msg);
static void randomSleep(); static void randomSleep();
void addAppData(const QString &data); void addAppData(const QString &data);
QStringList appData() const; QStringList appData() const;

View File

@@ -13,8 +13,6 @@
"--socket=fallback-x11", "--socket=fallback-x11",
"--device=dri", "--device=dri",
"--share=ipc", "--share=ipc",
"--share=network",
"--socket=cups",
"--filesystem=host" "--filesystem=host"
], ],
"modules": [ "modules": [

View File

@@ -1,5 +1,17 @@
#!/bin/sh #!/bin/sh
# check if _only_ the correct framework snap is connected
framework_name=$(grep "name:" $SNAP/kf5/meta/snap.yaml | cut -d" " -f2)
if [ "$framework_name" != "kde-frameworks-5-qt-5-15-3-core20" ]; then
downloads_dir=$(xdg-user-dir DOWNLOAD)
test ! -d "$downloads_dir" && mkdir -p "$downloads_dir"
instructions_file="snap.qelectrotech.update-instructions.html"
target="$downloads_dir/$instructions_file"
cp "$SNAP/update-instructions/$instructions_file" "$target"
xdg-open "$target"
exit 1
fi
# migrate .qet directory from SNAP_USER_DATA to SNAP_USER_COMMON # migrate .qet directory from SNAP_USER_DATA to SNAP_USER_COMMON
from="$SNAP_USER_DATA/.qet" from="$SNAP_USER_DATA/.qet"
to="$SNAP_USER_COMMON/.qet" to="$SNAP_USER_COMMON/.qet"

File diff suppressed because one or more lines are too long

View File

@@ -56,6 +56,12 @@ parts:
organize: organize:
'*': bin/ '*': bin/
update-instructions:
plugin: dump
source: build-aux/snap/local/update-instructions
organize:
'*': update-instructions/
qet-tb-generator: qet-tb-generator:
plugin: python plugin: python
source: https://github.com/raulroda/qet_tb_generator-plugin.git source: https://github.com/raulroda/qet_tb_generator-plugin.git
@@ -76,7 +82,7 @@ parts:
- rsync - rsync
override-build: | override-build: |
rsync -a --ignore-existing /snap/kde-frameworks-5-qt-5-15-3-core20-sdk/current/ / rsync -a --ignore-existing /snap/kde-frameworks-5-qt-5-15-3-core20-sdk/current/ /
dxf-to-qet: dxf-to-qet:
after: [kde-sdk-setup] after: [kde-sdk-setup]
plugin: nil plugin: nil
@@ -95,14 +101,16 @@ parts:
build-packages: build-packages:
- git - git
- libsqlite3-dev - libsqlite3-dev
override-build: | override-pull: |
snapcraftctl pull
displayed_version=$(grep "displayedVersion" sources/qet.h | tr -d "\";" | cut -d" " -f5) displayed_version=$(grep "displayedVersion" sources/qet.h | tr -d "\";" | cut -d" " -f5)
snap_version="${displayed_version}-g$(git rev-parse --short=8 HEAD)" snap_version="${displayed_version}~$(git rev-parse --short=8 HEAD)"
modified_displayed_version="${snap_version}.snap" modified_displayed_version="${snap_version}.snap"
sed -i -E "s|const QString displayedVersion =.*|const QString displayedVersion =\"$modified_displayed_version\";|" sources/qet.h sed -i -E "s|const QString displayedVersion =.*|const QString displayedVersion =\"$modified_displayed_version\";|" sources/qet.h
snapcraftctl set-version "$snap_version" snapcraftctl set-version "$snap_version"
override-build: |
qmake "$SNAPCRAFT_PART_SRC/qelectrotech.pro" qmake "$SNAPCRAFT_PART_SRC/qelectrotech.pro"
make -j${SNAPCRAFT_PARALLEL_BUILD_COUNT} make -j$(nproc)
make install INSTALL_ROOT="$SNAPCRAFT_PART_INSTALL" make install INSTALL_ROOT="$SNAPCRAFT_PART_INSTALL"
override-stage: | override-stage: |
snapcraftctl stage snapcraftctl stage

Some files were not shown because too many files have changed in this diff Show More