Improve UI around read-only mode / lock-files
See ticket: https://sourceforge.net/p/flightgear/codetickets/2133/ Give the user a chance to intervene, if we select read-only mode. Offer a button to clear the lock file if it’s stale, and start in read-write mode. This is still evolving, but want to get some feedback on it.
This commit is contained in:
parent
9d2e50fa56
commit
ddc7baada2
5 changed files with 88 additions and 42 deletions
|
@ -30,21 +30,15 @@
|
|||
#include <QProgressDialog>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QPixmap>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include <QCompleter>
|
||||
#include <QListView>
|
||||
#include <QSettings>
|
||||
#include <QUrl>
|
||||
#include <QAction>
|
||||
#include <QDateTime>
|
||||
#include <QApplication>
|
||||
#include <QSpinBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QThread>
|
||||
#include <QProcess>
|
||||
#include <QTranslator>
|
||||
#include <QMessageBox>
|
||||
|
||||
// Simgear
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
|
@ -506,6 +500,26 @@ bool runInAppLauncherDialog()
|
|||
return true;
|
||||
}
|
||||
|
||||
LockFileDialogResult showLockFileDialog()
|
||||
{
|
||||
QMessageBox mb;
|
||||
mb.setIconPixmap(QPixmap(":/app-icon-large"));
|
||||
mb.setWindowTitle("Multiple copies of Flightgear running");
|
||||
mb.setText("Flightgear has detected another copy is already running. "
|
||||
"This copy will run in read-only mode, so downloads will not be possible, "
|
||||
"and settings will not be saved.");
|
||||
mb.setInformativeText("If you are sure another copy is not running on this computer, "
|
||||
"you can choose to reset the lock file to prevent seeing this message again.");
|
||||
mb.addButton(QMessageBox::Ok);
|
||||
mb.setDefaultButton(QMessageBox::Ok);
|
||||
mb.addButton(QMessageBox::Reset);
|
||||
|
||||
int r = mb.exec();
|
||||
if (r == QMessageBox::Reset)
|
||||
return LockFileReset;
|
||||
return LockFileContinue;
|
||||
}
|
||||
|
||||
} // of namespace flightgear
|
||||
|
||||
#include "QtLauncher.moc"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// Written by James Turner, started December 2014.
|
||||
//
|
||||
// Copyright (C) 2014 James Turner <zakalawe@mac.com>
|
||||
// Copyright (C) 2014 James Turner <james@flightgear.org>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
|
@ -18,8 +18,9 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FG_QTLAUNCHER_HXX
|
||||
#define FG_QTLAUNCHER_HXX
|
||||
#pragma once
|
||||
|
||||
class SGPath;
|
||||
|
||||
namespace flightgear
|
||||
{
|
||||
|
@ -42,6 +43,14 @@ namespace flightgear
|
|||
|
||||
bool runInAppLauncherDialog();
|
||||
|
||||
enum LockFileDialogResult
|
||||
{
|
||||
LockFileContinue,
|
||||
LockFileReset
|
||||
};
|
||||
|
||||
LockFileDialogResult showLockFileDialog();
|
||||
|
||||
/**
|
||||
* @brief restartTheApp quit the application and relaunch it, passing the
|
||||
* --launcher flag explicitly.
|
||||
|
@ -50,5 +59,3 @@ namespace flightgear
|
|||
|
||||
void launcherSetSceneryPaths();
|
||||
}
|
||||
|
||||
#endif // of FG_QTLAUNCHER_HXX
|
||||
|
|
|
@ -456,7 +456,7 @@ SGPath fgHomePath()
|
|||
return SGPath::fromEnv("FG_HOME", platformDefaultDataPath());
|
||||
}
|
||||
|
||||
bool fgInitHome()
|
||||
InitHomeResult fgInitHome()
|
||||
{
|
||||
SGPath dataPath = fgHomePath();
|
||||
globals->set_fg_home(dataPath);
|
||||
|
@ -471,16 +471,16 @@ bool fgInitHome()
|
|||
"Problem setting up user data",
|
||||
"Unable to create the user-data storage folder at '" +
|
||||
dataPath.utf8Str() + "'.");
|
||||
return false;
|
||||
return InitHomeAbort;
|
||||
}
|
||||
|
||||
if (fgGetBool("/sim/fghome-readonly", false)) {
|
||||
// user / config forced us into readonly mode, fine
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Running with FG_HOME readonly");
|
||||
return true;
|
||||
return InitHomeExplicitReadOnly;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
InitHomeResult result = InitHomeOkay;
|
||||
#if defined(SG_WINDOWS)
|
||||
// don't use a PID file on Windows, because deleting on close is
|
||||
// unreliable and causes false-positives. Instead, use a named
|
||||
|
@ -490,14 +490,14 @@ bool fgInitHome()
|
|||
if (static_fgHomeWriteMutex == nullptr) {
|
||||
printf("CreateMutex error: %d\n", GetLastError());
|
||||
SG_LOG(SG_GENERAL, SG_POPUP, "Failed to create mutex for multi-app protection");
|
||||
return false;
|
||||
return InitHomeAbort;
|
||||
} else if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "flightgear instance already running, switching to FG_HOME read-only.");
|
||||
fgSetBool("/sim/fghome-readonly", true);
|
||||
return true;
|
||||
return InitHomeReadOnly;
|
||||
} else {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Created multi-app mutex, we are in writeable mode");
|
||||
result = true;
|
||||
result = InitHomeOkay;
|
||||
}
|
||||
#else
|
||||
// write our PID, and check writeability
|
||||
|
@ -509,7 +509,7 @@ bool fgInitHome()
|
|||
if (fd < 0) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "failed to open local file:" << pidPath
|
||||
<< "\n\tdue to:" << simgear::strutils::error_string(errno));
|
||||
return false;
|
||||
return InitHomeAbort;
|
||||
}
|
||||
|
||||
int err = ::flock(fd, LOCK_EX | LOCK_NB);
|
||||
|
@ -521,16 +521,16 @@ bool fgInitHome()
|
|||
// set a marker property so terrasync/navcache don't try to write
|
||||
// from secondary instances
|
||||
fgSetBool("/sim/fghome-readonly", true);
|
||||
return true;
|
||||
return InitHomeReadOnly;
|
||||
} else {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath
|
||||
<< "\n\tdue to:" << simgear::strutils::error_string(errno));
|
||||
return false;
|
||||
return InitHomeAbort;
|
||||
}
|
||||
}
|
||||
|
||||
// we locked it!
|
||||
result = true;
|
||||
result = InitHomeOkay;
|
||||
} else {
|
||||
char buf[16];
|
||||
std::string ps = pidPath.utf8Str();
|
||||
|
@ -540,24 +540,24 @@ bool fgInitHome()
|
|||
if (fd < 0) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "failed to open local file:" << pidPath
|
||||
<< "\n\tdue to:" << simgear::strutils::error_string(errno));
|
||||
return false;
|
||||
return InitHomeAbort;
|
||||
}
|
||||
|
||||
int err = write(fd, buf, len);
|
||||
if (err < 0) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "failed to write to lock file:" << pidPath
|
||||
<< "\n\tdue to:" << simgear::strutils::error_string(errno));
|
||||
return false;
|
||||
return InitHomeAbort;
|
||||
}
|
||||
|
||||
err = flock(fd, LOCK_EX);
|
||||
if (err != 0) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath
|
||||
<< "\n\tdue to:" << simgear::strutils::error_string(errno));
|
||||
return false;
|
||||
return InitHomeAbort;
|
||||
}
|
||||
|
||||
result = true;
|
||||
result = InitHomeOkay;
|
||||
}
|
||||
#endif
|
||||
fgSetBool("/sim/fghome-readonly", false);
|
||||
|
@ -578,6 +578,16 @@ void fgShutdownHome()
|
|||
#endif
|
||||
}
|
||||
|
||||
void fgDeleteLockFile()
|
||||
{
|
||||
#if defined(SG_WINDOWS)
|
||||
// there's no file here, so we can't actually delete anything
|
||||
#else
|
||||
SGPath pidPath = globals->get_fg_home() / "fgfs_lock.pid";
|
||||
pidPath.remove();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void createBaseStorageDirForAddons(const SGPath& exportDir)
|
||||
{
|
||||
SGPath addonStorageBasePath = exportDir / "Addons";
|
||||
|
|
|
@ -37,8 +37,17 @@ std::string fgBasePackageVersion(const SGPath& path);
|
|||
|
||||
SGPath fgHomePath();
|
||||
|
||||
bool fgInitHome();
|
||||
enum InitHomeResult
|
||||
{
|
||||
InitHomeOkay,
|
||||
InitHomeReadOnly,
|
||||
InitHomeExplicitReadOnly,
|
||||
InitHomeAbort
|
||||
};
|
||||
|
||||
InitHomeResult fgInitHome();
|
||||
void fgShutdownHome();
|
||||
void fgDeleteLockFile();
|
||||
|
||||
// Read in configuration (file and command line)
|
||||
int fgInitConfig ( int argc, char **argv, bool reinit );
|
||||
|
|
|
@ -530,14 +530,32 @@ int fgMainInit( int argc, char **argv )
|
|||
sglog().setStartupLoggingEnabled(true);
|
||||
|
||||
globals = new FGGlobals;
|
||||
if (!fgInitHome()) {
|
||||
return EXIT_FAILURE;
|
||||
auto initHomeResult = fgInitHome();
|
||||
if (initHomeResult == InitHomeAbort) {
|
||||
flightgear::fatalMessageBoxThenExit("Unable to create lock file",
|
||||
"Flightgear was unable to create the lock file in FG_HOME");
|
||||
}
|
||||
|
||||
#if defined(HAVE_QT)
|
||||
flightgear::initApp(argc, argv);
|
||||
#endif
|
||||
|
||||
// check if the launcher is requested, since it affects config file parsing
|
||||
bool showLauncher = flightgear::Options::checkForArg(argc, argv, "launcher");
|
||||
// an Info.plist bundle can't define command line arguments, but it can set
|
||||
// environment variables. This avoids needed a wrapper shell-script on OS-X.
|
||||
showLauncher |= (::getenv("FG_LAUNCHER") != nullptr);
|
||||
|
||||
if (showLauncher && (initHomeResult == InitHomeReadOnly)) {
|
||||
// show this message early, if we can
|
||||
auto r = flightgear::showLockFileDialog();
|
||||
if (r == flightgear::LockFileReset) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Deleting lock file at user request");
|
||||
fgDeleteLockFile();
|
||||
fgSetBool("/sim/fghome-readonly", false);
|
||||
}
|
||||
}
|
||||
|
||||
const bool readOnlyFGHome = fgGetBool("/sim/fghome-readonly");
|
||||
if (!readOnlyFGHome) {
|
||||
// now home is initialised, we can log to a file inside it
|
||||
|
@ -597,11 +615,6 @@ int fgMainInit( int argc, char **argv )
|
|||
upper_case_property("/sim/tower/airport-id");
|
||||
upper_case_property("/autopilot/route-manager/input");
|
||||
|
||||
// check if the launcher is requested, since it affects config file parsing
|
||||
bool showLauncher = flightgear::Options::checkForArg(argc, argv, "launcher");
|
||||
// an Info.plist bundle can't define command line arguments, but it can set
|
||||
// environment variables. This avoids needed a wrapper shell-script on OS-X.
|
||||
showLauncher |= (::getenv("FG_LAUNCHER") != nullptr);
|
||||
if (showLauncher) {
|
||||
// to minimise strange interactions when launcher and config files
|
||||
// set overlaping options, we disable the default files. Users can
|
||||
|
@ -609,13 +622,6 @@ int fgMainInit( int argc, char **argv )
|
|||
flightgear::Options::sharedInstance()->setShouldLoadDefaultConfig(false);
|
||||
}
|
||||
|
||||
if (showLauncher && readOnlyFGHome) {
|
||||
// this is perhaps not what the user wanted, let's inform them
|
||||
flightgear::modalMessageBox("Multiple copies of FlightGear",
|
||||
"Another copy of FlightGear is already running on this computer, "
|
||||
"so this copy will run in read-only mode.");
|
||||
}
|
||||
|
||||
// Load the configuration parameters. (Command line options
|
||||
// override config file options. Config file options override
|
||||
// defaults.)
|
||||
|
|
Loading…
Add table
Reference in a new issue