Start qt event loop for commandline usage

This is needed since src/support calls lots of qt code, and some parts of it
(e.g. QFileInfo) require a running event loop. This fixes bug #4917 which is
a symptom of the problem.
The fix is to create a QCoreApplication for tex2lyx, lyxclient and LyX running
without GUI. In the future this could be extended, i.e. to split up the
frontend Application class in a GUI and a console class, and having the
console class use LyXConsoleApp in the same way as Application now uses
GuiApplication. If that is done, one could also think of moving
support/ConsoleApplication to frontend/console/ConsoleApplication.
This commit is contained in:
Georg Baum 2015-01-11 14:06:17 +01:00
parent a02af82b82
commit 7b48e2aac0
8 changed files with 381 additions and 67 deletions

View File

@ -50,6 +50,7 @@
#include "frontends/alert.h"
#include "frontends/Application.h"
#include "support/ConsoleApplication.h"
#include "support/lassert.h"
#include "support/debug.h"
#include "support/environment.h"
@ -195,6 +196,27 @@ struct LyX::Impl {
};
/// The main application class for console mode
class LyXConsoleApp : public ConsoleApplication
{
public:
LyXConsoleApp(LyX * lyx, int & argc, char * argv[])
: ConsoleApplication(lyx_package, argc, argv), lyx_(lyx),
argc_(argc), argv_(argv)
{
}
void doExec()
{
int const exit_status = lyx_->execWithoutGui(argc_, argv_);
exit(exit_status);
}
private:
LyX * lyx_;
int & argc_;
char ** argv_;
};
///
frontend::Application * theApp()
{
@ -287,39 +309,13 @@ int LyX::exec(int & argc, char * argv[])
setLocale();
if (!use_gui) {
// FIXME: create a ConsoleApplication
int exit_status = init(argc, argv);
if (exit_status) {
prepareExit();
return exit_status;
}
LyXConsoleApp app(this, argc, argv);
// this is correct, since return values are inverted.
exit_status = !loadFiles();
// Reestablish our defaults, as Qt overwrites them
// after creating app
setLocale();//???
if (pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
prepareExit();
return exit_status;
}
BufferList::iterator begin = pimpl_->buffer_list_.begin();
bool final_success = false;
for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
Buffer * buf = *I;
if (buf != buf->masterBuffer())
continue;
vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
DispatchResult dr;
for (; bcit != bcend; ++bcit) {
LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
buf->dispatch(*bcit, dr);
final_success |= !dr.error();
}
}
prepareExit();
return !final_success;
return app.exec();
}
// Let the frontend parse and remove all arguments that it knows
@ -472,6 +468,43 @@ int LyX::init(int & argc, char * argv[])
}
int LyX::execWithoutGui(int & argc, char * argv[])
{
int exit_status = init(argc, argv);
if (exit_status) {
prepareExit();
return exit_status;
}
// this is correct, since return values are inverted.
exit_status = !loadFiles();
if (pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
prepareExit();
return exit_status;
}
BufferList::iterator begin = pimpl_->buffer_list_.begin();
bool final_success = false;
for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
Buffer * buf = *I;
if (buf != buf->masterBuffer())
continue;
vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
DispatchResult dr;
for (; bcit != bcend; ++bcit) {
LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
buf->dispatch(*bcit, dr);
final_success |= !dr.error();
}
}
prepareExit();
return !final_success;
}
bool LyX::loadFiles()
{
LATTEST(!use_gui);

View File

@ -60,8 +60,8 @@ class Application;
/// initial startup
class LyX {
friend class LyXConsoleApp;
public:
LyX();
~LyX();
@ -85,6 +85,9 @@ private:
*/
int init(int & argc, char * argv[]);
/// Execute commandline commands if no GUI was requested.
int execWithoutGui(int & argc, char * argv[]);
/// Execute batch commands if available.
void execCommands();

View File

@ -12,6 +12,7 @@
#include <config.h>
#include "support/ConsoleApplication.h"
#include "support/debug.h"
#include "support/FileName.h"
#include "support/FileNameList.h"
@ -54,6 +55,7 @@
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <string>
#include <vector>
#include <map>
@ -427,6 +429,16 @@ namespace cmdline {
docstring mainTmp(from_ascii("/tmp"));
class StopException : public exception
{
public:
StopException(int status) : status_(status) {}
int status() const { return status_; }
private:
int status_;
};
void usage()
{
cerr <<
@ -453,7 +465,7 @@ void usage()
int h(vector<docstring> const &)
{
usage();
exit(0);
throw StopException(EXIT_SUCCESS);
}
@ -548,15 +560,38 @@ int p(vector<docstring> const & arg)
} // namespace cmdline
} // namespace lyx
int main(int argc, char * argv[])
/// The main application class
class LyXClientApp : public ConsoleApplication
{
using namespace lyx;
lyxerr.setStream(cerr);
public:
LyXClientApp(int & argc, char * argv[])
: ConsoleApplication("client" PROGRAM_SUFFIX, argc, argv),
argc_(argc), argv_(argv)
{
}
void doExec()
{
try {
int const exit_status = run();
exit(exit_status);
}
catch (cmdline::StopException & e) {
exit(e.status());
}
}
private:
int run();
int & argc_;
char ** argv_;
};
int LyXClientApp::run()
{
// qt changes this, and our numeric conversions require the C locale
setlocale(LC_NUMERIC, "C");
// Set defaults
char const * const lyxsocket = getenv("LYXSOCKET");
if (lyxsocket)
@ -576,11 +611,11 @@ int main(int argc, char * argv[])
args.helper["-p"] = cmdline::p;
// Command line failure conditions:
if ((!args.parse(argc, argv))
if ((!args.parse(argc_, argv_))
|| (args.isset["-c"] && args.isset["-g"])
|| (args.isset["-a"] && args.isset["-p"])) {
cmdline::usage();
return 1;
return EXIT_FAILURE;
}
scoped_ptr<LyXDataSocket> server;
@ -677,6 +712,17 @@ int main(int argc, char * argv[])
return EXIT_SUCCESS;
}
} // namespace lyx
int main(int argc, char * argv[])
{
lyx::lyxerr.setStream(cerr);
lyx::LyXClientApp app(argc, argv);
return app.exec();
}
namespace boost {

View File

@ -0,0 +1,51 @@
/**
* \file ConsoleApplication.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Georg Baum
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "support/ConsoleApplication.h"
#include "support/ConsoleApplicationPrivate.h"
namespace lyx {
namespace support {
ConsoleApplication::~ConsoleApplication()
{
delete d;
}
ConsoleApplication::ConsoleApplication(std::string const & app,
int & argc, char ** argv)
: d(new ConsoleApplicationPrivate(this, app, argc, argv))
{
}
int ConsoleApplication::exec()
{
return d->execute();
}
void ConsoleApplication::exit(int status)
{
d->exit(status);
}
} // namespace support
} // namespace lyx
#include "moc_ConsoleApplicationPrivate.cpp"

View File

@ -0,0 +1,61 @@
/**
* \file ConsoleApplication.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Georg Baum
*
* Full author contact details are available in file CREDITS.
*/
#ifndef CONSOPLEAPPLICATION_H
#define CONSOPLEAPPLICATION_H
#include "support/strfwd.h"
namespace lyx {
namespace support {
class ConsoleApplicationPrivate;
/// The main application class for console mode.
/**
There should be only one instance of this class. No Qt object
initialisation should be done before the instantiation of this class.
This class could be moved to src/frontends/console in the future.
This would make sense if more console app related code is created.
*/
class ConsoleApplication
{
friend class ConsoleApplicationPrivate;
public:
ConsoleApplication(std::string const & app, int & argc, char ** argv);
virtual ~ConsoleApplication();
/// Start the event loop and execute the application
int exec();
protected:
/// Do the real work.
/// This is called after the event loop was started.
virtual void doExec() = 0;
/**
* Exit the application with status \p status.
* This must be called from doExec(), otherwise the application runs
* forever.
* Note that in contrast to the ISO C function ::exit() this method does
* return. It only registers that the program is to be stopped with the
* given status code, and this happens the next time the event loop is
* processed.
*/
void exit(int status);
private:
ConsoleApplicationPrivate * const d;
};
} // namespace support
} // namespace lyx
#endif // CONSOPLEAPPLICATION_H

View File

@ -0,0 +1,64 @@
/**
* \file ConsoleApplicationPrivate.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Georg Baum
*
* Full author contact details are available in file CREDITS.
*/
#ifndef CONSOPLEAPPLICATIONPRIVATE_H
#define CONSOPLEAPPLICATIONPRIVATE_H
#include "support/qstring_helpers.h"
#include <QCoreApplication>
#include <QDateTime>
#include <QTimer>
#include <string>
namespace lyx {
namespace support {
class ConsoleApplication;
class ConsoleApplicationPrivate : public QCoreApplication
{
Q_OBJECT
public:
ConsoleApplicationPrivate(ConsoleApplication * owner,
std::string const & app, int & argc, char ** argv)
: QCoreApplication(argc, argv), owner_(owner)
{
setOrganizationName("LyX");
setOrganizationDomain("lyx.org");
setApplicationName(toqstr(app));
qsrand(QDateTime::currentDateTime().toTime_t());
}
int execute()
{
// set timer to do the work asynchronously after the event
// loop was started
QTimer::singleShot(0, this, SLOT(doExec()));
// start event loop
return exec();
}
private Q_SLOTS:
void doExec()
{
owner_->doExec();
}
private:
ConsoleApplication * owner_;
};
} // namespace support
} // namespace lyx
#endif // CONSOPLEAPPLICATIONPRIVATE_H

View File

@ -13,7 +13,9 @@ BUILT_SOURCES = $(PCH_FILE)
######################### Qt stuff #############################
#
MOCHEADER = SystemcallPrivate.h
MOCHEADER = \
ConsoleApplicationPrivate.h \
SystemcallPrivate.h
MOCEDFILES = $(MOCHEADER:%.h=moc_%.cpp)
@ -36,6 +38,9 @@ liblyxsupport_a_SOURCES = \
FileMonitor.cpp \
RandomAccessList.h \
bind.h \
ConsoleApplication.cpp \
ConsoleApplication.h \
ConsoleApplicationPrivate.h \
convert.cpp \
convert.h \
copied_ptr.h \

View File

@ -24,6 +24,7 @@
#include "Preamble.h"
#include "TextClass.h"
#include "support/ConsoleApplication.h"
#include "support/convert.h"
#include "support/ExceptionMessage.h"
#include "support/filetools.h"
@ -35,6 +36,7 @@
#include <cstdlib>
#include <algorithm>
#include <exception>
#include <iostream>
#include <string>
#include <sstream>
@ -531,6 +533,44 @@ int error_code = 0;
typedef int (*cmd_helper)(string const &, string const &);
class StopException : public exception
{
public:
StopException(int status) : status_(status) {}
int status() const { return status_; }
private:
int status_;
};
/// The main application class
class TeX2LyXApp : public ConsoleApplication
{
public:
TeX2LyXApp(int & argc, char * argv[])
: ConsoleApplication("tex2lyx" PROGRAM_SUFFIX, argc, argv),
argc_(argc), argv_(argv)
{
}
void doExec()
{
try {
int const exit_status = run();
exit(exit_status);
}
catch (StopException & e) {
exit(e.status());
}
}
private:
void easyParse();
/// Do the real work
int run();
int & argc_;
char ** argv_;
};
int parse_help(string const &, string const &)
{
cout << "Usage: tex2lyx [options] infile.tex [outfile.lyx]\n"
@ -558,7 +598,7 @@ int parse_help(string const &, string const &)
"\tand \"SYSDIR/layouts\" are searched for layout and module files.\n"
"Check the tex2lyx man page for more details."
<< endl;
exit(error_code);
throw StopException(error_code);
}
@ -570,14 +610,14 @@ int parse_version(string const &, string const &)
<< endl;
cout << lyx_version_info << endl;
exit(error_code);
throw StopException(error_code);
}
void error_message(string const & message)
{
cerr << "tex2lyx: " << message << "\n\n";
error_code = 1;
error_code = EXIT_FAILURE;
parse_help(string(), string());
}
@ -687,7 +727,7 @@ int parse_copyfiles(string const &, string const &)
}
void easyParse(int & argc, char * argv[])
void TeX2LyXApp::easyParse()
{
map<string, cmd_helper> cmdmap;
@ -710,29 +750,29 @@ void easyParse(int & argc, char * argv[])
cmdmap["-roundtrip"] = parse_roundtrip;
cmdmap["-copyfiles"] = parse_copyfiles;
for (int i = 1; i < argc; ++i) {
for (int i = 1; i < argc_; ++i) {
map<string, cmd_helper>::const_iterator it
= cmdmap.find(argv[i]);
= cmdmap.find(argv_[i]);
// don't complain if not found - may be parsed later
if (it == cmdmap.end()) {
if (argv[i][0] == '-')
error_message(string("Unknown option `") + argv[i] + "'.");
if (argv_[i][0] == '-')
error_message(string("Unknown option `") + argv_[i] + "'.");
else
continue;
}
string arg = (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
string arg2 = (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
string arg = (i + 1 < argc_) ? os::utf8_argv(i + 1) : string();
string arg2 = (i + 2 < argc_) ? os::utf8_argv(i + 2) : string();
int const remove = 1 + it->second(arg, arg2);
// Now, remove used arguments by shifting
// the following ones remove places down.
os::remove_internal_args(i, remove);
argc -= remove;
for (int j = i; j < argc; ++j)
argv[j] = argv[j + remove];
argc_ -= remove;
for (int j = i; j < argc_; ++j)
argv_[j] = argv_[j + remove];
--i;
}
}
@ -960,18 +1000,13 @@ bool tex2tex(string const & infilename, FileName const & outfilename,
return false;
}
} // namespace lyx
namespace {
int main(int argc, char * argv[])
int TeX2LyXApp::run()
{
using namespace lyx;
//setlocale(LC_CTYPE, "");
lyxerr.setStream(cerr);
os::init(argc, argv);
// qt changes this, and our numeric conversions require the C locale
setlocale(LC_NUMERIC, "C");
try {
init_package(internal_path(os::utf8_argv(0)), string(), string());
@ -982,9 +1017,9 @@ int main(int argc, char * argv[])
return EXIT_FAILURE;
}
easyParse(argc, argv);
easyParse();
if (argc <= 1)
if (argc_ <= 1)
error_message("Not enough arguments.");
try {
@ -1017,7 +1052,7 @@ int main(int argc, char * argv[])
infilename = makeAbsPath(infilename).absFileName();
string outfilename;
if (argc > 2) {
if (argc_ > 2) {
outfilename = internal_path(os::utf8_argv(2));
if (outfilename != "-")
outfilename = makeAbsPath(outfilename).absFileName();
@ -1107,11 +1142,27 @@ int main(int argc, char * argv[])
if (tex2tex(infilename, FileName(outfilename), default_encoding))
return EXIT_SUCCESS;
} else {
if (tex2lyx(infilename, FileName(outfilename), default_encoding))
if (lyx::tex2lyx(infilename, FileName(outfilename), default_encoding))
return EXIT_SUCCESS;
}
}
return EXIT_FAILURE;
}
} // anonymous namespace
} // namespace lyx
int main(int argc, char * argv[])
{
//setlocale(LC_CTYPE, "");
lyx::lyxerr.setStream(cerr);
os::init(argc, argv);
lyx::TeX2LyXApp app(argc, argv);
return app.exec();
}
// }])