mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-12-22 05:16:21 +00:00
Add a Qt version of the LyX server monitor program.
I tried to account for msvc based on the MS docs, but I cannot test it. Apologies if it does not compile out of the box. It works with mingw. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@31372 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
68809af49b
commit
f20d6d1eab
387
development/lyxserver/server_monitor.cpp
Normal file
387
development/lyxserver/server_monitor.cpp
Normal file
@ -0,0 +1,387 @@
|
||||
/**
|
||||
* \file server_monitor.cpp
|
||||
* This file is part of LyX, the document processor.
|
||||
* Licence details can be found in the file COPYING.
|
||||
*
|
||||
* \author Enrico Forestieri
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*
|
||||
* This program sends commands to a running instance of LyX and
|
||||
* receives information back from LyX.
|
||||
*
|
||||
* Build instructions:
|
||||
* 1) Run moc or moc-qt4 on server_monitor.h to produce moc_server_monitor.cpp:
|
||||
* moc-qt4 server_monitor.h -o moc_server_monitor.cpp
|
||||
* 2) If the QtGui.pc file is not in the pkg-config search path, find the
|
||||
* directory where it is located (e.g., use the command `locate QtGui.pc')
|
||||
* and set the environment variable PKG_CONFIG_PATH to this directory.
|
||||
* For example:
|
||||
* export PKG_CONFIG_PATH=/path/to/directory (if using bash)
|
||||
* setenv PKG_CONFIG_PATH /path/to/directory (if using tcsh)
|
||||
* If the command `pkg-config --modversion QtGui' does not complain and
|
||||
* prints the Qt version, you don't need to set PKG_CONFIG_PATH.
|
||||
* 3) Compile using the following command:
|
||||
* g++ server_monitor.cpp -o monitor -I. `pkg-config --cflags --libs QtGui`
|
||||
*
|
||||
* Usage:
|
||||
* 1) Set the LyXserver pipe path in the LyX preferences (on *nix you can use
|
||||
* any path, for example ~/.lyx/lyxpipe, whereas on Windows the path has
|
||||
* to start with `\\.\pipe\', for example you can use \\.\pipe\lyxpipe).
|
||||
* 2) Quit and restart LyX.
|
||||
* 3) Launch this program, adjust the pipe name to match that one used in LyX,
|
||||
* push the button labeled "Open pipes" and then try issuing some commands.
|
||||
*/
|
||||
|
||||
#include "server_monitor.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QtGui>
|
||||
#include <QtDebug>
|
||||
|
||||
class ReadPipe : public QThread {
|
||||
public:
|
||||
///
|
||||
ReadPipe(LyXServerMonitor * monitor) : lyxmonitor(monitor) {}
|
||||
///
|
||||
void run() { lyxmonitor->readPipe(); }
|
||||
|
||||
private:
|
||||
///
|
||||
LyXServerMonitor * lyxmonitor;
|
||||
};
|
||||
|
||||
|
||||
class DeleteThread : public QEvent {
|
||||
public:
|
||||
///
|
||||
DeleteThread(ReadPipe * thread)
|
||||
: QEvent(QEvent::User), pipethread(thread)
|
||||
{}
|
||||
///
|
||||
ReadPipe * pipeThread() const { return pipethread; }
|
||||
|
||||
private:
|
||||
///
|
||||
ReadPipe * pipethread;
|
||||
};
|
||||
|
||||
|
||||
LyXServerMonitor::LyXServerMonitor()
|
||||
: pipein(-1), pipeout(-1), thread_exit(false), lyx_listen(false)
|
||||
{
|
||||
createGridGroupBox();
|
||||
createCmdsGroupBox();
|
||||
|
||||
char const * const home = getenv("HOME");
|
||||
QString const pipeName = (home && home[0]) ?
|
||||
QString::fromUtf8(home) + "/.lyx/lyxpipe" : "\\\\.\\pipe\\lyxpipe";
|
||||
|
||||
pipeNameLE->setText(pipeName);
|
||||
clientNameLE->setText("monitor");
|
||||
submitCommandPB->setDisabled(true);
|
||||
closePipesPB->setDisabled(true);
|
||||
|
||||
connect(openPipesPB, SIGNAL(clicked()), this, SLOT(openPipes()));
|
||||
connect(closePipesPB, SIGNAL(clicked()), this, SLOT(closePipes()));
|
||||
connect(submitCommandPB, SIGNAL(clicked()), this, SLOT(submitCommand()));
|
||||
connect(donePB, SIGNAL(clicked()), this, SLOT(reject()));
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(gridGB);
|
||||
mainLayout->addWidget(horizontalGB);
|
||||
setLayout(mainLayout);
|
||||
|
||||
setWindowTitle("LyX Server Monitor");
|
||||
}
|
||||
|
||||
|
||||
LyXServerMonitor::~LyXServerMonitor()
|
||||
{
|
||||
if (pipein != -1)
|
||||
closePipes();
|
||||
}
|
||||
|
||||
|
||||
void LyXServerMonitor::createGridGroupBox()
|
||||
{
|
||||
gridGB = new QGroupBox;
|
||||
QGridLayout *layout = new QGridLayout;
|
||||
|
||||
labels[0] = new QLabel("Pipe name");
|
||||
pipeNameLE = new QLineEdit;
|
||||
layout->addWidget(labels[0], 0, 0, Qt::AlignRight);
|
||||
layout->addWidget(pipeNameLE, 0, 1);
|
||||
|
||||
labels[1] = new QLabel("Command");
|
||||
commandLE = new QLineEdit;
|
||||
layout->addWidget(labels[1], 1, 0, Qt::AlignRight);
|
||||
layout->addWidget(commandLE, 1, 1);
|
||||
|
||||
labels[2] = new QLabel("Client name");
|
||||
clientNameLE = new QLineEdit;
|
||||
layout->addWidget(labels[2], 0, 2, Qt::AlignRight);
|
||||
layout->addWidget(clientNameLE, 0, 3);
|
||||
|
||||
labels[3] = new QLabel("Argument");
|
||||
argumentLE = new QLineEdit;
|
||||
layout->addWidget(labels[3], 1, 2, Qt::AlignRight);
|
||||
layout->addWidget(argumentLE, 1, 3);
|
||||
|
||||
labels[4] = new QLabel("Info");
|
||||
infoLB = new QLabel;
|
||||
infoLB->setFrameStyle(QFrame::Panel | QFrame::Sunken);
|
||||
infoLB->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
layout->addWidget(labels[4], 2, 0, Qt::AlignRight);
|
||||
layout->addWidget(infoLB, 2, 1, 1, 3);
|
||||
|
||||
labels[5] = new QLabel("Notify");
|
||||
notifyLB = new QLabel;
|
||||
notifyLB->setFrameStyle(QFrame::Panel | QFrame::Sunken);
|
||||
notifyLB->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
layout->addWidget(labels[5], 3, 0, Qt::AlignRight);
|
||||
layout->addWidget(notifyLB, 3, 1, 1, 3);
|
||||
|
||||
layout->setColumnMinimumWidth(1, 200);
|
||||
layout->setColumnMinimumWidth(3, 200);
|
||||
gridGB->setLayout(layout);
|
||||
}
|
||||
|
||||
|
||||
void LyXServerMonitor::createCmdsGroupBox()
|
||||
{
|
||||
horizontalGB = new QGroupBox;
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
|
||||
openPipesPB = new QPushButton("&Open pipes");
|
||||
layout->addWidget(openPipesPB);
|
||||
|
||||
closePipesPB = new QPushButton("C&lose pipes");
|
||||
layout->addWidget(closePipesPB);
|
||||
|
||||
submitCommandPB = new QPushButton("&Submit Command");
|
||||
layout->addWidget(submitCommandPB);
|
||||
|
||||
donePB = new QPushButton("&Done");
|
||||
layout->addWidget(donePB);
|
||||
|
||||
horizontalGB->setLayout(layout);
|
||||
}
|
||||
|
||||
|
||||
void LyXServerMonitor::readPipe()
|
||||
{
|
||||
int n;
|
||||
errno = 0;
|
||||
bool notified = false;
|
||||
|
||||
while ((n = ::read(pipeout, pipedata, BUFSIZE - 1)) && !thread_exit) {
|
||||
if (n > 0) {
|
||||
pipedata[n] = 0;
|
||||
QString const fromLyX =
|
||||
QString::fromUtf8(pipedata).trimmed();
|
||||
qWarning() << "monitor: Coming: " << fromLyX;
|
||||
if (fromLyX.startsWith("LYXSRV:")) {
|
||||
if (fromLyX.contains("bye")) {
|
||||
qWarning() << "monitor: LyX has closed "
|
||||
"connection!";
|
||||
infoLB->clear();
|
||||
notifyLB->setText(fromLyX);
|
||||
notified = true;
|
||||
break;
|
||||
}
|
||||
if (fromLyX.contains("hello")) {
|
||||
lyx_listen = true;
|
||||
qWarning() << "monitor: "
|
||||
"LyX is listening!";
|
||||
submitCommandPB->setDisabled(false);
|
||||
}
|
||||
}
|
||||
if (fromLyX[0] == QLatin1Char('I')) {
|
||||
infoLB->setText(fromLyX);
|
||||
notifyLB->clear();
|
||||
} else {
|
||||
infoLB->clear();
|
||||
notifyLB->setText(fromLyX);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
// On Windows, we have to close and reopen
|
||||
// the pipe after each use.
|
||||
::close(pipeout);
|
||||
pipeout = ::open(
|
||||
outPipeName().toLocal8Bit().constData(),
|
||||
O_RDONLY);
|
||||
if (pipeout < 0) {
|
||||
perror("monitor");
|
||||
infoLB->clear();
|
||||
notifyLB->setText("An error occurred, "
|
||||
"closing pipes");
|
||||
notified = true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
} else if (n < 0) {
|
||||
#ifdef __CYGWIN__
|
||||
if (errno == ECOMM) {
|
||||
// When talking to a native Windows version of
|
||||
// LyX, the second time we try to use the pipe,
|
||||
// read() fails with ECOMM. In this case, we
|
||||
// have to simply close and reopen it.
|
||||
::close(pipeout);
|
||||
pipeout = ::open(
|
||||
outPipeName().toLocal8Bit().constData(),
|
||||
O_RDONLY);
|
||||
if (pipeout >= 0)
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
perror("monitor");
|
||||
infoLB->clear();
|
||||
notifyLB->setText("An error occurred, closing pipes");
|
||||
notified = true;
|
||||
break;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (!notified) {
|
||||
if (thread_exit) {
|
||||
qWarning() << "monitor: Closing pipes";
|
||||
infoLB->clear();
|
||||
notifyLB->setText("Closing pipes");
|
||||
} else {
|
||||
qWarning() << "monitor: LyX has closed connection!";
|
||||
infoLB->clear();
|
||||
notifyLB->setText("LyX has closed connection!");
|
||||
}
|
||||
}
|
||||
DeleteThread * event = new DeleteThread(pipethread);
|
||||
QCoreApplication::postEvent(this, static_cast<QEvent *>(event));
|
||||
lyx_listen = false;
|
||||
closePipes();
|
||||
}
|
||||
|
||||
|
||||
bool LyXServerMonitor::event(QEvent * e)
|
||||
{
|
||||
if (e->type() == QEvent::User) {
|
||||
ReadPipe * pipeThread =
|
||||
static_cast<DeleteThread *>(e)->pipeThread();
|
||||
pipeThread->wait();
|
||||
thread_exit = false;
|
||||
delete pipeThread;
|
||||
return true;
|
||||
}
|
||||
return QDialog::event(e);
|
||||
}
|
||||
|
||||
|
||||
void LyXServerMonitor::openPipes()
|
||||
{
|
||||
if (pipein == -1) {
|
||||
qWarning() << "monitor: Opening pipes " << inPipeName()
|
||||
<< " and " << outPipeName();
|
||||
pipein = ::open(inPipeName().toLocal8Bit().constData(),
|
||||
O_WRONLY);
|
||||
pipeout = ::open(outPipeName().toLocal8Bit().constData(),
|
||||
O_RDONLY);
|
||||
if (pipein < 0 || pipeout < 0) {
|
||||
qWarning() << "monitor: Could not open the pipes";
|
||||
infoLB->clear();
|
||||
notifyLB->setText("Could not open the pipes");
|
||||
if (pipein >= 0 || pipeout >= 0)
|
||||
closePipes();
|
||||
return;
|
||||
}
|
||||
pipethread = new ReadPipe(this);
|
||||
pipethread->start();
|
||||
if (!pipethread->isRunning()) {
|
||||
qWarning() << "monitor: Could not create pipe thread";
|
||||
infoLB->clear();
|
||||
notifyLB->setText("Could not create pipe thread");
|
||||
closePipes();
|
||||
return;
|
||||
}
|
||||
openPipesPB->setDisabled(true);
|
||||
closePipesPB->setDisabled(false);
|
||||
// greet LyX
|
||||
QString const clientname = clientNameLE->text();
|
||||
snprintf(buffer, BUFSIZE - 1,
|
||||
"LYXSRV:%s:hello\n", clientname.toUtf8().constData());
|
||||
buffer[BUFSIZE - 1] = '\0';
|
||||
::write(pipein, buffer, strlen(buffer));
|
||||
} else
|
||||
qWarning() << "monitor: Pipes already opened, close them first\n";
|
||||
}
|
||||
|
||||
|
||||
void LyXServerMonitor::closePipes()
|
||||
{
|
||||
if (pipein == -1 && pipeout == -1) {
|
||||
qWarning() << "monitor: Pipes are not opened";
|
||||
return;
|
||||
}
|
||||
|
||||
if (pipein >= 0) {
|
||||
if (lyx_listen) {
|
||||
lyx_listen = false;
|
||||
QString const clientname = clientNameLE->text();
|
||||
if (pipethread->isRunning()) {
|
||||
thread_exit = true;
|
||||
// The thread, currently blocked on the read()
|
||||
// call, will be waked up by the reply from
|
||||
// LyX and will exit.
|
||||
snprintf(buffer, BUFSIZE - 1,
|
||||
"LYXCMD:%s:message:Client '%s' is leaving\n",
|
||||
clientname.toUtf8().constData(),
|
||||
clientname.toUtf8().constData());
|
||||
buffer[BUFSIZE - 1] = '\0';
|
||||
::write(pipein, buffer, strlen(buffer));
|
||||
}
|
||||
/* Say goodbye */
|
||||
snprintf(buffer, BUFSIZE - 1, "LYXSRV:%s:bye\n",
|
||||
clientname.toUtf8().constData());
|
||||
buffer[BUFSIZE - 1] = '\0';
|
||||
::write(pipein, buffer, strlen(buffer));
|
||||
pipethread->wait();
|
||||
thread_exit = false;
|
||||
}
|
||||
::close(pipein);
|
||||
}
|
||||
|
||||
if (pipeout >= 0)
|
||||
::close(pipeout);
|
||||
pipein = pipeout = -1;
|
||||
submitCommandPB->setDisabled(true);
|
||||
openPipesPB->setDisabled(false);
|
||||
closePipesPB->setDisabled(true);
|
||||
}
|
||||
|
||||
|
||||
void LyXServerMonitor::submitCommand()
|
||||
{
|
||||
if (pipein >= 0) {
|
||||
QString const command = commandLE->text();
|
||||
QString const argument = argumentLE->text();
|
||||
QString const clientname = clientNameLE->text();
|
||||
snprintf(buffer, BUFSIZE - 2, "LYXCMD:%s:%s:%s",
|
||||
clientname.toUtf8().constData(),
|
||||
command.toUtf8().constData(),
|
||||
argument.toUtf8().constData());
|
||||
buffer[BUFSIZE - 1] = '\0';
|
||||
qWarning() << "monitor: Sending: " << buffer;
|
||||
strcat(buffer, "\n");
|
||||
::write(pipein, buffer, strlen(buffer));
|
||||
} else
|
||||
qWarning() << "monitor: Pipe is not opened";
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
LyXServerMonitor dialog;
|
||||
return dialog.exec();
|
||||
}
|
||||
|
||||
#include "moc_server_monitor.cpp"
|
90
development/lyxserver/server_monitor.h
Normal file
90
development/lyxserver/server_monitor.h
Normal file
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* \file server_monitor.h
|
||||
* This file is part of LyX, the document processor.
|
||||
* Licence details can be found in the file COPYING.
|
||||
*
|
||||
* \author Enrico Forestieri
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
|
||||
#ifndef SERVER_MONITOR_H
|
||||
#define SERVER_MONITOR_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QFile>
|
||||
#include <QLineEdit>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#ifdef _MSC_VER
|
||||
#include <io.h>
|
||||
#define open _open
|
||||
#define close _close
|
||||
#define read _read
|
||||
#define write _write
|
||||
#define snprintf _snprintf
|
||||
#define O_RDONLY _O_RDONLY
|
||||
#define O_WRONLY _O_WRONLY
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class QGroupBox;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class ReadPipe;
|
||||
|
||||
class LyXServerMonitor : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum { BUFSIZE = 512 };
|
||||
|
||||
public:
|
||||
LyXServerMonitor();
|
||||
///
|
||||
~LyXServerMonitor();
|
||||
///
|
||||
void readPipe();
|
||||
///
|
||||
QString inPipeName() { return pipeNameLE->text() + ".in"; }
|
||||
///
|
||||
QString outPipeName() { return pipeNameLE->text() + ".out"; }
|
||||
|
||||
public Q_SLOTS:
|
||||
void openPipes();
|
||||
void closePipes();
|
||||
void submitCommand();
|
||||
|
||||
private:
|
||||
void createCmdsGroupBox();
|
||||
void createGridGroupBox();
|
||||
bool event(QEvent *);
|
||||
|
||||
QGroupBox * horizontalGB;
|
||||
QGroupBox * gridGB;
|
||||
QLabel * labels[6];
|
||||
QLineEdit * pipeNameLE;
|
||||
QLineEdit * clientNameLE;
|
||||
QLineEdit * commandLE;
|
||||
QLineEdit * argumentLE;
|
||||
QLabel * infoLB;
|
||||
QLabel * notifyLB;
|
||||
QPushButton * openPipesPB;
|
||||
QPushButton * closePipesPB;
|
||||
QPushButton * submitCommandPB;
|
||||
QPushButton * donePB;
|
||||
|
||||
int pipein;
|
||||
int pipeout;
|
||||
bool thread_exit;
|
||||
bool lyx_listen;
|
||||
char buffer[BUFSIZE];
|
||||
char pipedata[BUFSIZE];
|
||||
ReadPipe * pipethread;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user