aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/core/libLumina/LuminaSingleApplication.cpp
blob: 379ac02dea639b310796ed6703f0dc3476d6f086 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//===========================================
//  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 <QDir>
#include <QFile>
#include <QLocalSocket>
#include <QDebug>
#include <QX11Info>

#include <unistd.h> //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::number(getuid());
  //For locking the process use the official process name - not the user input (no masking)
  appname = this->applicationName();
  QString display = QString(getenv("DISPLAY"));
  if(display.startsWith(":")){ display.remove(0,1); }
  display = display.section(".",0,0);
  cfile = cfile.arg( username, appname, display );
  lockfile = new QLockFile(cfile+"-lock");
    lockfile->setStaleLockTime(0); //long-lived processes
  for(int i=1; i<argc; i++){
    QString path = QString::fromLocal8Bit(argv[i]);
    //do few quick conversions for relative paths and such as necessary
    // (Remember: this is only used for secondary processes, not the primary)
      if(path=="."){
	//Insert the current working directory instead
	path = QDir::currentPath();
      }else{
	if(!path.startsWith("/") && !path.startsWith("-") ){ path.prepend(QDir::currentPath()+"/"); }
      }
    inputlist << path;
  }
  isActive = isBypass = false;
  lserver = 0;
  //Now check for the manual CLI flag to bypass single-instance forwarding (if necessary)
  if(inputlist.contains("-new-instance")){
    isBypass = true;
    inputlist.removeAll("-new-instance");
  }
  PerformLockChecks();
}

LSingleApplication::~LSingleApplication(){
  if(lserver != 0 && lockfile->isLocked() ){
    //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);
  }
}
bgstack15