+ implement mechanism to allow only a single application instance
This commit is contained in:
parent
899199daf5
commit
3d8d6eca5e
|
@ -1285,49 +1285,57 @@ void Application::initApplication(void)
|
|||
Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADInit"));
|
||||
}
|
||||
|
||||
void Application::processCmdLineFiles(void)
|
||||
std::list<std::string> Application::getCmdLineFiles()
|
||||
{
|
||||
Base::Console().Log("Init: Processing command line files\n");
|
||||
std::list<std::string> files;
|
||||
|
||||
// cycling through all the open files
|
||||
unsigned short count = 0;
|
||||
count = atoi(mConfig["OpenFileCount"].c_str());
|
||||
std::string File;
|
||||
|
||||
if (count == 0 && mConfig["RunMode"] == "Exit")
|
||||
mConfig["RunMode"] = "Cmd";
|
||||
|
||||
for (unsigned short i=0; i<count; i++) {
|
||||
// getting file name
|
||||
std::ostringstream temp;
|
||||
temp << "OpenFile" << i;
|
||||
|
||||
FileInfo File(mConfig[temp.str()].c_str());
|
||||
std::string file(mConfig[temp.str()]);
|
||||
files.push_back(file);
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
void Application::processFiles(const std::list<std::string>& files)
|
||||
{
|
||||
Base::Console().Log("Init: Processing command line files\n");
|
||||
for (std::list<std::string>::const_iterator it = files.begin(); it != files.end(); ++it) {
|
||||
Base::FileInfo file(*it);
|
||||
|
||||
Base::Console().Log("Init: Processing file: %s\n",file.filePath().c_str());
|
||||
|
||||
std::string Ext = File.extension();
|
||||
Base::Console().Log("Init: Processing file: %s\n",File.filePath().c_str());
|
||||
try {
|
||||
|
||||
if (File.hasExtension("fcstd") || File.hasExtension("std")) {
|
||||
if (file.hasExtension("fcstd") || file.hasExtension("std")) {
|
||||
// try to open
|
||||
Application::_pcSingleton->openDocument(File.filePath().c_str());
|
||||
Application::_pcSingleton->openDocument(file.filePath().c_str());
|
||||
}
|
||||
else if (File.hasExtension("fcscript")||File.hasExtension("fcmacro")) {
|
||||
Base::Interpreter().runFile(File.filePath().c_str(), true);
|
||||
else if (file.hasExtension("fcscript") || file.hasExtension("fcmacro")) {
|
||||
Base::Interpreter().runFile(file.filePath().c_str(), true);
|
||||
}
|
||||
else if (File.hasExtension("py")) {
|
||||
else if (file.hasExtension("py")) {
|
||||
try {
|
||||
Base::Interpreter().loadModule(File.fileNamePure().c_str());
|
||||
Base::Interpreter().loadModule(file.fileNamePure().c_str());
|
||||
}
|
||||
catch(const PyException&) {
|
||||
// if module load not work, just try run the script (run in __main__)
|
||||
Base::Interpreter().runFile(File.filePath().c_str(),true);
|
||||
Base::Interpreter().runFile(file.filePath().c_str(),true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::vector<std::string> mods = App::GetApplication().getImportModules(Ext.c_str());
|
||||
std::string ext = file.extension();
|
||||
std::vector<std::string> mods = App::GetApplication().getImportModules(ext.c_str());
|
||||
if (!mods.empty()) {
|
||||
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(File.filePath().c_str());
|
||||
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(file.filePath().c_str());
|
||||
Base::Interpreter().loadModule(mods.front().c_str());
|
||||
Base::Interpreter().runStringArg("import %s",mods.front().c_str());
|
||||
Base::Interpreter().runStringArg("%s.open(u\"%s\")",mods.front().c_str(),
|
||||
|
@ -1335,7 +1343,7 @@ void Application::processCmdLineFiles(void)
|
|||
Base::Console().Log("Command line open: %s.open(u\"%s\")\n",mods.front().c_str(),escapedstr.c_str());
|
||||
}
|
||||
else {
|
||||
Console().Warning("File format not supported: %s \n", File.filePath().c_str());
|
||||
Console().Warning("File format not supported: %s \n", file.filePath().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1343,12 +1351,24 @@ void Application::processCmdLineFiles(void)
|
|||
throw; // re-throw to main() function
|
||||
}
|
||||
catch (const Base::Exception& e) {
|
||||
Console().Error("Exception while processing file: %s [%s]\n", File.filePath().c_str(), e.what());
|
||||
Console().Error("Exception while processing file: %s [%s]\n", file.filePath().c_str(), e.what());
|
||||
}
|
||||
catch (...) {
|
||||
Console().Error("Unknown exception while processing file: %s \n", File.filePath().c_str());
|
||||
Console().Error("Unknown exception while processing file: %s \n", file.filePath().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::processCmdLineFiles(void)
|
||||
{
|
||||
// process files passed to command line
|
||||
std::list<std::string> files = getCmdLineFiles();
|
||||
processFiles(files);
|
||||
|
||||
if (files.empty()) {
|
||||
if (mConfig["RunMode"] == "Exit")
|
||||
mConfig["RunMode"] = "Cmd";
|
||||
}
|
||||
|
||||
const std::map<std::string,std::string>& cfg = Application::Config();
|
||||
std::map<std::string,std::string>::const_iterator it = cfg.find("SaveFile");
|
||||
|
@ -1586,6 +1606,7 @@ void Application::ParseOptions(int ac, char ** av)
|
|||
("run-test,t", value<int>() ,"Test level")
|
||||
("module-path,M", value< vector<string> >()->composing(),"Additional module paths")
|
||||
("python-path,P", value< vector<string> >()->composing(),"Additional python paths")
|
||||
("single-instance", "Allow to run a single instance of the application")
|
||||
;
|
||||
|
||||
|
||||
|
@ -1806,6 +1827,10 @@ void Application::ParseOptions(int ac, char ** av)
|
|||
};
|
||||
}
|
||||
|
||||
if (vm.count("single-instance")) {
|
||||
mConfig["SingleInstance"] = "1";
|
||||
}
|
||||
|
||||
if (vm.count("dump-config")) {
|
||||
std::stringstream str;
|
||||
for (std::map<std::string,std::string>::iterator it=mConfig.begin(); it != mConfig.end(); ++it) {
|
||||
|
|
|
@ -216,6 +216,8 @@ public:
|
|||
static void destruct(void);
|
||||
static void destructObserver(void);
|
||||
static void processCmdLineFiles(void);
|
||||
static std::list<std::string> getCmdLineFiles();
|
||||
static void processFiles(const std::list<std::string>&);
|
||||
static void runApplication(void);
|
||||
friend Application &GetApplication(void);
|
||||
static std::map<std::string,std::string> &Config(void){return mConfig;}
|
||||
|
|
|
@ -1538,15 +1538,34 @@ void Application::initTypes(void)
|
|||
|
||||
void Application::runApplication(void)
|
||||
{
|
||||
const std::map<std::string,std::string>& cfg = App::Application::Config();
|
||||
std::map<std::string,std::string>::const_iterator it;
|
||||
|
||||
// A new QApplication
|
||||
Base::Console().Log("Init: Creating Gui::Application and QApplication\n");
|
||||
// if application not yet created by the splasher
|
||||
int argc = App::Application::GetARGC();
|
||||
int systemExit = 1000;
|
||||
GUIApplication mainApp(argc, App::Application::GetARGV(), systemExit);
|
||||
GUISingleApplication mainApp(argc, App::Application::GetARGV(), systemExit);
|
||||
|
||||
// check if a single or multiple instances can run
|
||||
it = cfg.find("SingleInstance");
|
||||
if (it != cfg.end() && mainApp.isRunning()) {
|
||||
// send the file names to be opened to the server application so that this
|
||||
// opens them
|
||||
std::list<std::string> files = App::Application::getCmdLineFiles();
|
||||
for (std::list<std::string>::iterator jt = files.begin(); jt != files.end(); ++jt) {
|
||||
QByteArray msg(jt->c_str(), static_cast<int>(jt->size()));
|
||||
msg.prepend("OpenFile:");
|
||||
if (!mainApp.sendMessage(msg)) {
|
||||
qWarning("Failed to send message to server");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// set application icon and window title
|
||||
const std::map<std::string,std::string>& cfg = App::Application::Config();
|
||||
std::map<std::string,std::string>::const_iterator it;
|
||||
it = cfg.find("Application");
|
||||
if (it != cfg.end()) {
|
||||
mainApp.setApplicationName(QString::fromUtf8(it->second.c_str()));
|
||||
|
@ -1615,6 +1634,8 @@ void Application::runApplication(void)
|
|||
Application app(true);
|
||||
MainWindow mw;
|
||||
mw.setWindowTitle(mainApp.applicationName());
|
||||
QObject::connect(&mainApp, SIGNAL(messageReceived(const QList<QByteArray> &)),
|
||||
&mw, SLOT(processMessages(const QList<QByteArray> &)));
|
||||
|
||||
ParameterGrp::handle hDocGrp = WindowParameter::getDefaultParameter()->GetGroup("Document");
|
||||
int timeout = hDocGrp->GetInt("AutoSaveTimeout", 15); // 15 min
|
||||
|
@ -1741,19 +1762,8 @@ void Application::runApplication(void)
|
|||
|
||||
Instance->d->startingUp = false;
|
||||
|
||||
#if 0
|
||||
// processing all command line files
|
||||
App::Application::processCmdLineFiles();
|
||||
|
||||
// Create new document?
|
||||
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("Document");
|
||||
if (hGrp->GetBool("CreateNewDoc", false)) {
|
||||
App::GetApplication().newDocument();
|
||||
}
|
||||
#else
|
||||
// gets called once we start the event loop
|
||||
QTimer::singleShot(0, &mw, SLOT(delayedStartup()));
|
||||
#endif
|
||||
|
||||
// run the Application event loop
|
||||
Base::Console().Log("Init: Entering event loop\n");
|
||||
|
|
|
@ -26,7 +26,24 @@
|
|||
#ifndef _PreComp_
|
||||
# include <sstream>
|
||||
# include <stdexcept>
|
||||
# include <QByteArray>
|
||||
# include <QDataStream>
|
||||
# include <QDebug>
|
||||
# include <QFileInfo>
|
||||
# include <QSessionManager>
|
||||
# include <QTimer>
|
||||
#endif
|
||||
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
# include <Windows.h>
|
||||
#endif
|
||||
#if defined(Q_OS_UNIX)
|
||||
# include <sys/types.h>
|
||||
# include <time.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "GuiApplication.h"
|
||||
|
@ -46,6 +63,10 @@ GUIApplication::GUIApplication(int & argc, char ** argv, int exitcode)
|
|||
{
|
||||
}
|
||||
|
||||
GUIApplication::~GUIApplication()
|
||||
{
|
||||
}
|
||||
|
||||
bool GUIApplication::notify (QObject * receiver, QEvent * event)
|
||||
{
|
||||
if (!receiver && event) {
|
||||
|
@ -123,4 +144,140 @@ void GUIApplication::commitData(QSessionManager &manager)
|
|||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class GUISingleApplication::Private {
|
||||
public:
|
||||
Private(GUISingleApplication *q_ptr)
|
||||
: q_ptr(q_ptr)
|
||||
, timer(new QTimer(q_ptr))
|
||||
, server(0)
|
||||
, running(false)
|
||||
{
|
||||
timer->setSingleShot(true);
|
||||
std::string exeName = App::GetApplication().getExecutableName();
|
||||
serverName = QString::fromStdString(exeName);
|
||||
}
|
||||
|
||||
~Private()
|
||||
{
|
||||
if (server)
|
||||
server->close();
|
||||
delete server;
|
||||
}
|
||||
|
||||
void setupConnection()
|
||||
{
|
||||
QLocalSocket socket;
|
||||
socket.connectToServer(serverName);
|
||||
if (socket.waitForConnected(1000)) {
|
||||
this->running = true;
|
||||
}
|
||||
else {
|
||||
startServer();
|
||||
}
|
||||
}
|
||||
|
||||
void startServer()
|
||||
{
|
||||
// Start a QLocalServer to listen for connections
|
||||
server = new QLocalServer();
|
||||
QObject::connect(server, SIGNAL(newConnection()),
|
||||
q_ptr, SLOT(receiveConnection()));
|
||||
// first attempt
|
||||
if (!server->listen(serverName)) {
|
||||
if (server->serverError() == QAbstractSocket::AddressInUseError) {
|
||||
// second attempt
|
||||
server->removeServer(serverName);
|
||||
server->listen(serverName);
|
||||
}
|
||||
}
|
||||
if (server->isListening()) {
|
||||
Base::Console().Log("Local server '%s' started\n", qPrintable(serverName));
|
||||
}
|
||||
else {
|
||||
Base::Console().Log("Local server '%s' failed to start\n", qPrintable(serverName));
|
||||
}
|
||||
}
|
||||
|
||||
GUISingleApplication *q_ptr;
|
||||
QTimer *timer;
|
||||
QLocalServer *server;
|
||||
QString serverName;
|
||||
QList<QByteArray> messages;
|
||||
bool running;
|
||||
};
|
||||
|
||||
GUISingleApplication::GUISingleApplication(int & argc, char ** argv, int exitcode)
|
||||
: GUIApplication(argc, argv, exitcode),
|
||||
d_ptr(new Private(this))
|
||||
{
|
||||
d_ptr->setupConnection();
|
||||
connect(d_ptr->timer, SIGNAL(timeout()), this, SLOT(processMessages()));
|
||||
}
|
||||
|
||||
GUISingleApplication::~GUISingleApplication()
|
||||
{
|
||||
}
|
||||
|
||||
bool GUISingleApplication::isRunning() const
|
||||
{
|
||||
return d_ptr->running;
|
||||
}
|
||||
|
||||
bool GUISingleApplication::sendMessage(const QByteArray &message, int timeout)
|
||||
{
|
||||
QLocalSocket socket;
|
||||
bool connected = false;
|
||||
for(int i = 0; i < 2; i++) {
|
||||
socket.connectToServer(d_ptr->serverName);
|
||||
connected = socket.waitForConnected(timeout/2);
|
||||
if (connected || i > 0)
|
||||
break;
|
||||
int ms = 250;
|
||||
#if defined(Q_OS_WIN)
|
||||
Sleep(DWORD(ms));
|
||||
#else
|
||||
usleep(ms*1000);
|
||||
#endif
|
||||
}
|
||||
if (!connected)
|
||||
return false;
|
||||
|
||||
QDataStream ds(&socket);
|
||||
ds << message;
|
||||
socket.waitForBytesWritten(timeout);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GUISingleApplication::receiveConnection()
|
||||
{
|
||||
QLocalSocket *socket = d_ptr->server->nextPendingConnection();
|
||||
if (!socket)
|
||||
return;
|
||||
|
||||
connect(socket, SIGNAL(disconnected()),
|
||||
socket, SLOT(deleteLater()));
|
||||
if (socket->waitForReadyRead()) {
|
||||
QDataStream in(socket);
|
||||
if (!in.atEnd()) {
|
||||
d_ptr->timer->stop();
|
||||
QByteArray message;
|
||||
in >> message;
|
||||
Base::Console().Log("Received message: %s\n", message.constData());
|
||||
d_ptr->messages.push_back(message);
|
||||
d_ptr->timer->start(1000);
|
||||
}
|
||||
}
|
||||
|
||||
socket->disconnectFromServer();
|
||||
}
|
||||
|
||||
void GUISingleApplication::processMessages()
|
||||
{
|
||||
QList<QByteArray> msg = d_ptr->messages;
|
||||
d_ptr->messages.clear();
|
||||
Q_EMIT messageReceived(msg);
|
||||
}
|
||||
|
||||
#include "moc_GuiApplication.cpp"
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#define GUI_APPLICATION_H
|
||||
|
||||
#include "GuiApplicationNativeEventAware.h"
|
||||
#include <QList>
|
||||
|
||||
class QSessionManager;
|
||||
|
||||
|
@ -38,7 +39,8 @@ class GUIApplication : public GUIApplicationNativeEventAware
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GUIApplication(int & argc, char ** argv, int exitcode);
|
||||
explicit GUIApplication(int & argc, char ** argv, int exitcode);
|
||||
virtual ~GUIApplication();
|
||||
|
||||
/**
|
||||
* Make forwarding events exception-safe and get more detailed information
|
||||
|
@ -51,6 +53,29 @@ private:
|
|||
int systemExit;
|
||||
};
|
||||
|
||||
class GUISingleApplication : public GUIApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GUISingleApplication(int & argc, char ** argv, int exitcode);
|
||||
virtual ~GUISingleApplication();
|
||||
|
||||
bool isRunning() const;
|
||||
bool sendMessage(const QByteArray &message, int timeout = 5000);
|
||||
|
||||
private Q_SLOTS:
|
||||
void receiveConnection();
|
||||
void processMessages();
|
||||
|
||||
Q_SIGNALS:
|
||||
void messageReceived(const QList<QByteArray> &);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
QScopedPointer<Private> d_ptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // GUI_APPLICATION_H
|
||||
|
|
|
@ -999,6 +999,23 @@ void MainWindow::showMainWindow()
|
|||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::processMessages(const QList<QByteArray> & msg)
|
||||
{
|
||||
// handle all the messages to open files
|
||||
try {
|
||||
WaitCursor wc;
|
||||
std::list<std::string> files;
|
||||
QByteArray action("OpenFile:");
|
||||
for (QList<QByteArray>::const_iterator it = msg.begin(); it != msg.end(); ++it) {
|
||||
if (it->startsWith(action))
|
||||
files.push_back(std::string(it->mid(action.size()).constData()));
|
||||
}
|
||||
App::Application::processFiles(files);
|
||||
}
|
||||
catch (const Base::SystemExitException&) {
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::delayedStartup()
|
||||
{
|
||||
// processing all command line files
|
||||
|
|
|
@ -262,6 +262,10 @@ private Q_SLOTS:
|
|||
* \internal
|
||||
*/
|
||||
void delayedStartup();
|
||||
/**
|
||||
* \internal
|
||||
*/
|
||||
void processMessages(const QList<QByteArray> &);
|
||||
|
||||
Q_SIGNALS:
|
||||
void timeEvent();
|
||||
|
|
Loading…
Reference in New Issue
Block a user