2 * \file server_monitor.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Enrico Forestieri
8 * Full author contact details are available in file CREDITS.
10 * This program sends commands to a running instance of LyX and
11 * receives information back from LyX.
14 * 1) Run moc or moc-qt4 on server_monitor.h to produce moc_server_monitor.cpp:
15 * moc-qt4 server_monitor.h -o moc_server_monitor.cpp
16 * 2) If the QtGui.pc file is not in the pkg-config search path, find the
17 * directory where it is located (e.g., use the command `locate QtGui.pc')
18 * and set the environment variable PKG_CONFIG_PATH to this directory.
20 * export PKG_CONFIG_PATH=/path/to/directory (if using bash)
21 * setenv PKG_CONFIG_PATH /path/to/directory (if using tcsh)
22 * If the command `pkg-config --modversion QtGui' does not complain and
23 * prints the Qt version, you don't need to set PKG_CONFIG_PATH.
24 * 3) Compile using the following command:
25 * g++ server_monitor.cpp -o monitor -I. `pkg-config --cflags --libs QtGui`
27 * Alternatively, you can create a Makefile with qmake and then build
28 * the executable by running make (or nmake, if you use msvc):
33 * 1) Set the LyXserver pipe path in the LyX preferences (on *nix you can use
34 * any path, for example ~/.lyx/lyxpipe, whereas on Windows the path has
35 * to start with `\\.\pipe\', for example you can use \\.\pipe\lyxpipe).
36 * 2) Quit and restart LyX.
37 * 3) Launch this program, adjust the pipe name to match that one used in LyX,
38 * push the button labeled "Open pipes" and then try issuing some commands.
41 #include <QApplication>
44 #if QT_VERSION >= 0x050000
48 #include "server_monitor.h"
50 LyXServerMonitor::LyXServerMonitor()
51 : pipein(-1), pipeout(-1), thread_exit(false), lyx_listen(false)
56 char const * const home = getenv("HOME");
57 QString const pipeName = (home && home[0]) ?
58 QString::fromUtf8(home) + "/.lyx/lyxpipe" : "\\\\.\\pipe\\lyxpipe";
60 pipeNameLE->setText(pipeName);
61 clientNameLE->setText("monitor");
62 submitCommandPB->setDisabled(true);
63 closePipesPB->setDisabled(true);
65 connect(openPipesPB, SIGNAL(clicked()), this, SLOT(openPipes()));
66 connect(closePipesPB, SIGNAL(clicked()), this, SLOT(closePipes()));
67 connect(submitCommandPB, SIGNAL(clicked()), this, SLOT(submitCommand()));
68 connect(donePB, SIGNAL(clicked()), this, SLOT(reject()));
70 QVBoxLayout * mainLayout = new QVBoxLayout;
71 mainLayout->addWidget(gridGB);
72 mainLayout->addWidget(horizontalGB);
73 setLayout(mainLayout);
75 setWindowTitle("LyX Server Monitor");
79 LyXServerMonitor::~LyXServerMonitor()
86 void LyXServerMonitor::createGridGroupBox()
88 gridGB = new QGroupBox;
89 QGridLayout * layout = new QGridLayout;
91 labels[0] = new QLabel("Pipe name");
92 pipeNameLE = new QLineEdit;
93 layout->addWidget(labels[0], 0, 0, Qt::AlignRight);
94 layout->addWidget(pipeNameLE, 0, 1);
96 labels[1] = new QLabel("Command");
97 commandLE = new QLineEdit;
98 layout->addWidget(labels[1], 1, 0, Qt::AlignRight);
99 layout->addWidget(commandLE, 1, 1);
101 labels[2] = new QLabel("Client name");
102 clientNameLE = new QLineEdit;
103 layout->addWidget(labels[2], 0, 2, Qt::AlignRight);
104 layout->addWidget(clientNameLE, 0, 3);
106 labels[3] = new QLabel("Argument");
107 argumentLE = new QLineEdit;
108 layout->addWidget(labels[3], 1, 2, Qt::AlignRight);
109 layout->addWidget(argumentLE, 1, 3);
111 labels[4] = new QLabel("Info");
113 infoLB->setFrameStyle(QFrame::Panel | QFrame::Sunken);
114 infoLB->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
115 layout->addWidget(labels[4], 2, 0, Qt::AlignRight);
116 layout->addWidget(infoLB, 2, 1, 1, 3);
118 labels[5] = new QLabel("Notify");
119 notifyLB = new QLabel;
120 notifyLB->setFrameStyle(QFrame::Panel | QFrame::Sunken);
121 notifyLB->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
122 layout->addWidget(labels[5], 3, 0, Qt::AlignRight);
123 layout->addWidget(notifyLB, 3, 1, 1, 3);
125 layout->setColumnMinimumWidth(1, 200);
126 layout->setColumnMinimumWidth(3, 200);
127 gridGB->setLayout(layout);
131 void LyXServerMonitor::createCmdsGroupBox()
133 horizontalGB = new QGroupBox;
134 QHBoxLayout * layout = new QHBoxLayout;
136 openPipesPB = new QPushButton("&Open pipes");
137 layout->addWidget(openPipesPB);
139 closePipesPB = new QPushButton("C&lose pipes");
140 layout->addWidget(closePipesPB);
142 submitCommandPB = new QPushButton("&Submit Command");
143 layout->addWidget(submitCommandPB);
145 donePB = new QPushButton("&Done");
146 layout->addWidget(donePB);
148 horizontalGB->setLayout(layout);
152 void LyXServerMonitor::readPipe()
156 bool notified = false;
158 while ((n = ::read(pipeout, pipedata, BUFSIZE - 1)) && !thread_exit) {
161 QString const fromLyX =
162 QString::fromUtf8(pipedata).trimmed();
163 qWarning() << "monitor: Coming: " << fromLyX;
164 if (fromLyX.startsWith("LYXSRV:")) {
165 if (fromLyX.contains("bye")) {
166 qWarning() << "monitor: LyX has closed "
168 pipethread->emitNotice(fromLyX);
172 if (fromLyX.contains("hello")) {
174 qWarning() << "monitor: "
176 submitCommandPB->setDisabled(false);
179 if (fromLyX[0] == QLatin1Char('I'))
180 pipethread->emitInfo(fromLyX);
182 pipethread->emitNotice(fromLyX);
184 // On Windows, we have to close and reopen
185 // the pipe after each use.
188 outPipeName().toLocal8Bit().constData(),
192 pipethread->emitNotice("An error occurred, "
200 if (errno == ECOMM) {
201 // When talking to a native Windows version of
202 // LyX, the second time we try to use the pipe,
203 // read() fails with ECOMM. In this case, we
204 // have to simply close and reopen it.
207 outPipeName().toLocal8Bit().constData(),
214 pipethread->emitNotice("An error occurred, closing pipes");
223 qWarning() << "monitor: Closing pipes";
224 pipethread->emitNotice("Closing pipes");
226 qWarning() << "monitor: LyX has closed connection!";
227 pipethread->emitNotice("LyX has closed connection!");
230 QEvent * event = new QEvent(QEvent::User);
231 QCoreApplication::postEvent(this, event);
234 pipethread->emitClosing();
238 bool LyXServerMonitor::event(QEvent * e)
240 if (e->type() == QEvent::User) {
246 return QDialog::event(e);
250 void LyXServerMonitor::showInfo(QString const & msg)
252 infoLB->setText(msg);
257 void LyXServerMonitor::showNotice(QString const & msg)
260 notifyLB->setText(msg);
264 void LyXServerMonitor::openPipes()
267 qWarning() << "monitor: Opening pipes " << inPipeName()
268 << " and " << outPipeName();
269 pipein = ::open(inPipeName().toLocal8Bit().constData(),
271 pipeout = ::open(outPipeName().toLocal8Bit().constData(),
273 if (pipein < 0 || pipeout < 0) {
274 qWarning() << "monitor: Could not open the pipes";
276 notifyLB->setText("Could not open the pipes");
277 if (pipein >= 0 || pipeout >= 0)
281 pipethread = new ReadPipe(this);
283 if (!pipethread->isRunning()) {
284 qWarning() << "monitor: Could not create pipe thread";
286 notifyLB->setText("Could not create pipe thread");
290 connect(pipethread, SIGNAL(info(QString const &)),
291 this, SLOT(showInfo(QString const &)));
292 connect(pipethread, SIGNAL(notice(QString const &)),
293 this, SLOT(showNotice(QString const &)));
294 connect(pipethread, SIGNAL(closing()),
295 this, SLOT(closePipes()));
296 openPipesPB->setDisabled(true);
297 closePipesPB->setDisabled(false);
299 QString const clientname = clientNameLE->text();
300 snprintf(buffer, BUFSIZE - 1,
301 "LYXSRV:%s:hello\n", clientname.toUtf8().constData());
302 buffer[BUFSIZE - 1] = '\0';
303 ::write(pipein, buffer, strlen(buffer));
305 qWarning() << "monitor: Pipes already opened, close them first\n";
309 void LyXServerMonitor::closePipes()
311 if (pipein == -1 && pipeout == -1) {
312 qWarning() << "monitor: Pipes are not opened";
319 QString const clientname = clientNameLE->text();
320 if (pipethread->isRunning()) {
322 // The thread, currently blocked on the read()
323 // call, will be waked up by the reply from
324 // LyX and will exit.
325 snprintf(buffer, BUFSIZE - 1,
326 "LYXCMD:%s:message:Client '%s' is leaving\n",
327 clientname.toUtf8().constData(),
328 clientname.toUtf8().constData());
329 buffer[BUFSIZE - 1] = '\0';
330 ::write(pipein, buffer, strlen(buffer));
333 snprintf(buffer, BUFSIZE - 1, "LYXSRV:%s:bye\n",
334 clientname.toUtf8().constData());
335 buffer[BUFSIZE - 1] = '\0';
336 ::write(pipein, buffer, strlen(buffer));
345 pipein = pipeout = -1;
346 submitCommandPB->setDisabled(true);
347 openPipesPB->setDisabled(false);
348 closePipesPB->setDisabled(true);
352 void LyXServerMonitor::submitCommand()
355 QString const command = commandLE->text();
356 QString const argument = argumentLE->text();
357 QString const clientname = clientNameLE->text();
358 snprintf(buffer, BUFSIZE - 2, "LYXCMD:%s:%s:%s",
359 clientname.toUtf8().constData(),
360 command.toUtf8().constData(),
361 argument.toUtf8().constData());
362 buffer[BUFSIZE - 1] = '\0';
363 qWarning() << "monitor: Sending: " << buffer;
364 strcat(buffer, "\n");
365 ::write(pipein, buffer, strlen(buffer));
367 qWarning() << "monitor: Pipe is not opened";
371 int main(int argc, char * argv[])
373 QApplication app(argc, argv);
374 LyXServerMonitor dialog;
375 return dialog.exec();
378 #include "moc_server_monitor.cpp"