//=========================================== // Lumina-DE source code // Copyright (c) 2014, Ken Moore // Available under the 3-clause BSD license // See the LICENSE file for full details //=========================================== #include "LuminaSingleApplication.h" #include #include #include #include #include #include //for getuid() LSingleApplication::LSingleApplication(int &argc, char **argv, QString appname) : QApplication(argc, argv){ //Load the proper translation systems this->setAttribute(Qt::AA_UseHighDpiPixmaps); if(appname!="lumina-desktop"){ cTrans = LUtils::LoadTranslation(this, appname); }//save the translator for later //Initialize a couple convenience internal variables cfile = QDir::tempPath()+"/.LSingleApp-%1-%2-%3"; QString username = QString::fromNumber(getuid()); //For locking the process use the official process name - not the user input (no masking) appname = this->applicationName(); cfile = cfile.arg( username, appname, QString::number(QX11Info::appScreen()) ); lockfile = new QLockFile(cfile+"-lock"); lockfile->setStaleLockTime(0); //long-lived processes for(int i=1; iisLocked() ){ //currently locked instance: remove the lock now lserver->close(); QLocalServer::removeServer(cfile); lockfile->unlock(); } } bool LSingleApplication::isPrimaryProcess(){ return (isActive || isBypass); } void LSingleApplication::PerformLockChecks(){ bool primary = lockfile->tryLock(); //qDebug() << "Try Lock: " << primary; if(!primary){ //Pre-existing lock - check it for validity QString appname, hostname; qint64 pid; lockfile->getLockInfo(&pid, &hostname, &appname); //PID already exists if it gets this far, ignore hostname //qDebug() << " - Lock Info:" << pid << hostname << appname; if( appname!=this->applicationName() || !QFile::exists(cfile) ){ //Some other process has the same PID or the server does not exist - stale lock qDebug() << " - Cleaning stale single-instance lock:"; if(lockfile->removeStaleLockFile() ){ if(QFile::exists(cfile)){ QLocalServer::removeServer(cfile); } //also remove stale socket/server file }else{ qDebug() << " -- Could not remove lock file"; } //Now re-try to create the lock primary = lockfile->tryLock(); //qDebug() << " - Try Lock Again:" << primary; } } if(primary || !QFile::exists(cfile) ){ //Create the server socket //qDebug() << "Create Local Server"; if(QFile::exists(cfile)){ QLocalServer::removeServer(cfile); } //stale socket/server file lserver = new QLocalServer(this); connect(lserver, SIGNAL(newConnection()), this, SLOT(newInputsAvailable()) ); if( lserver->listen(cfile) ){ qDebug() << " - Created new single-instance lock"; lserver->setSocketOptions(QLocalServer::UserAccessOption); //qDebug() << " - Success"; isActive = true; }else{ qDebug() << " - WARNING: Could not create single-instance framework"; qDebug() << " - Falling back on standard application startup"; lockfile->unlock(); isActive = true; } }else if(!isBypass){ //forward the current inputs to the locked process for processing and exit //Check the connection to the local server first qDebug() << "Single-instance lock found"; QLocalSocket socket(this); socket.connectToServer(cfile); socket.waitForConnected(); if(!socket.isValid() || socket.state()!=QLocalSocket::ConnectedState){ //error - could not forward info for some reason qDebug() << " - Could not connect to locking process: exiting..."; exit(1); } qDebug() << " - Forwarding inputs to locking process and closing down this instance..."; socket.write( inputlist.join("::::").toLocal8Bit() ); socket.waitForDisconnected(500); //max out at 1/2 second (only hits this if no inputs) } } //New messages detected void LSingleApplication::newInputsAvailable(){ while(lserver->hasPendingConnections()){ QLocalSocket *sock = lserver->nextPendingConnection(); QByteArray bytes; sock->waitForReadyRead(); while(sock->bytesAvailable() > 0){ //if(sock->waitForReadyRead()){ //qDebug() << "Info Available"; bytes.append( sock->readAll() ); } sock->disconnectFromServer(); QStringList inputs = QString::fromLocal8Bit(bytes).split("::::"); //qDebug() << " - New Inputs Detected:" << inputs; emit InputsAvailable(inputs); } }