commit 7b48e2aac0d63fe23cbeadf6ddd50ab0c26c1b6e
Author: Georg Baum <[email protected]>
Date: Sun Jan 11 14:06:17 2015 +0100
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.
diff --git a/src/LyX.cpp b/src/LyX.cpp
index 0a21b4b..8b75b44 100644
--- a/src/LyX.cpp
+++ b/src/LyX.cpp
@@ -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;
- }
-
- // this is correct, since return values are inverted.
- exit_status = !loadFiles();
+ LyXConsoleApp app(this, argc, argv);
- if (pimpl_->batch_commands.empty() ||
pimpl_->buffer_list_.empty()) {
- prepareExit();
- return exit_status;
- }
+ // Reestablish our defaults, as Qt overwrites them
+ // after creating app
+ setLocale();//???
- 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);
diff --git a/src/LyX.h b/src/LyX.h
index 70b8b7e..ccce7a3 100644
--- a/src/LyX.h
+++ b/src/LyX.h
@@ -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();
diff --git a/src/client/client.cpp b/src/client/client.cpp
index c781bc6..4d7eea5 100644
--- a/src/client/client.cpp
+++ b/src/client/client.cpp
@@ -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 {
diff --git a/src/support/ConsoleApplication.cpp
b/src/support/ConsoleApplication.cpp
new file mode 100644
index 0000000..ace4fc5
--- /dev/null
+++ b/src/support/ConsoleApplication.cpp
@@ -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"
diff --git a/src/support/ConsoleApplication.h b/src/support/ConsoleApplication.h
new file mode 100644
index 0000000..bfb5d07
--- /dev/null
+++ b/src/support/ConsoleApplication.h
@@ -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
diff --git a/src/support/ConsoleApplicationPrivate.h
b/src/support/ConsoleApplicationPrivate.h
new file mode 100644
index 0000000..74aa161
--- /dev/null
+++ b/src/support/ConsoleApplicationPrivate.h
@@ -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
diff --git a/src/support/Makefile.am b/src/support/Makefile.am
index fe7be80..b843b51 100644
--- a/src/support/Makefile.am
+++ b/src/support/Makefile.am
@@ -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 \
diff --git a/src/tex2lyx/tex2lyx.cpp b/src/tex2lyx/tex2lyx.cpp
index dc46770..2104854 100644
--- a/src/tex2lyx/tex2lyx.cpp
+++ b/src/tex2lyx/tex2lyx.cpp
@@ -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();
+}
+
// }])