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>
45 #include "server_monitor.h"
47 class ReadPipe : public QThread {
50 ReadPipe(LyXServerMonitor * monitor) : lyxmonitor(monitor) {}
52 void run() { lyxmonitor->readPipe(); }
56 LyXServerMonitor * lyxmonitor;
60 class DeleteThread : public QEvent {
63 DeleteThread(ReadPipe * thread)
64 : QEvent(QEvent::User), pipethread(thread)
67 ReadPipe * pipeThread() const { return pipethread; }
71 ReadPipe * pipethread;
75 LyXServerMonitor::LyXServerMonitor()
76 : pipein(-1), pipeout(-1), thread_exit(false), lyx_listen(false)
81 char const * const home = getenv("HOME");
82 QString const pipeName = (home && home[0]) ?
83 QString::fromUtf8(home) + "/.lyx/lyxpipe" : "\\\\.\\pipe\\lyxpipe";
85 pipeNameLE->setText(pipeName);
86 clientNameLE->setText("monitor");
87 submitCommandPB->setDisabled(true);
88 closePipesPB->setDisabled(true);
90 connect(openPipesPB, SIGNAL(clicked()), this, SLOT(openPipes()));
91 connect(closePipesPB, SIGNAL(clicked()), this, SLOT(closePipes()));
92 connect(submitCommandPB, SIGNAL(clicked()), this, SLOT(submitCommand()));
93 connect(donePB, SIGNAL(clicked()), this, SLOT(reject()));
95 QVBoxLayout * mainLayout = new QVBoxLayout;
96 mainLayout->addWidget(gridGB);
97 mainLayout->addWidget(horizontalGB);
98 setLayout(mainLayout);
100 setWindowTitle("LyX Server Monitor");
104 LyXServerMonitor::~LyXServerMonitor()
111 void LyXServerMonitor::createGridGroupBox()
113 gridGB = new QGroupBox;
114 QGridLayout * layout = new QGridLayout;
116 labels[0] = new QLabel("Pipe name");
117 pipeNameLE = new QLineEdit;
118 layout->addWidget(labels[0], 0, 0, Qt::AlignRight);
119 layout->addWidget(pipeNameLE, 0, 1);
121 labels[1] = new QLabel("Command");
122 commandLE = new QLineEdit;
123 layout->addWidget(labels[1], 1, 0, Qt::AlignRight);
124 layout->addWidget(commandLE, 1, 1);
126 labels[2] = new QLabel("Client name");
127 clientNameLE = new QLineEdit;
128 layout->addWidget(labels[2], 0, 2, Qt::AlignRight);
129 layout->addWidget(clientNameLE, 0, 3);
131 labels[3] = new QLabel("Argument");
132 argumentLE = new QLineEdit;
133 layout->addWidget(labels[3], 1, 2, Qt::AlignRight);
134 layout->addWidget(argumentLE, 1, 3);
136 labels[4] = new QLabel("Info");
138 infoLB->setFrameStyle(QFrame::Panel | QFrame::Sunken);
139 infoLB->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
140 layout->addWidget(labels[4], 2, 0, Qt::AlignRight);
141 layout->addWidget(infoLB, 2, 1, 1, 3);
143 labels[5] = new QLabel("Notify");
144 notifyLB = new QLabel;
145 notifyLB->setFrameStyle(QFrame::Panel | QFrame::Sunken);
146 notifyLB->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
147 layout->addWidget(labels[5], 3, 0, Qt::AlignRight);
148 layout->addWidget(notifyLB, 3, 1, 1, 3);
150 layout->setColumnMinimumWidth(1, 200);
151 layout->setColumnMinimumWidth(3, 200);
152 gridGB->setLayout(layout);
156 void LyXServerMonitor::createCmdsGroupBox()
158 horizontalGB = new QGroupBox;
159 QHBoxLayout * layout = new QHBoxLayout;
161 openPipesPB = new QPushButton("&Open pipes");
162 layout->addWidget(openPipesPB);
164 closePipesPB = new QPushButton("C&lose pipes");
165 layout->addWidget(closePipesPB);
167 submitCommandPB = new QPushButton("&Submit Command");
168 layout->addWidget(submitCommandPB);
170 donePB = new QPushButton("&Done");
171 layout->addWidget(donePB);
173 horizontalGB->setLayout(layout);
177 void LyXServerMonitor::readPipe()
181 bool notified = false;
183 while ((n = ::read(pipeout, pipedata, BUFSIZE - 1)) && !thread_exit) {
186 QString const fromLyX =
187 QString::fromUtf8(pipedata).trimmed();
188 qWarning() << "monitor: Coming: " << fromLyX;
189 if (fromLyX.startsWith("LYXSRV:")) {
190 if (fromLyX.contains("bye")) {
191 qWarning() << "monitor: LyX has closed "
194 notifyLB->setText(fromLyX);
198 if (fromLyX.contains("hello")) {
200 qWarning() << "monitor: "
202 submitCommandPB->setDisabled(false);
205 if (fromLyX[0] == QLatin1Char('I')) {
206 infoLB->setText(fromLyX);
210 notifyLB->setText(fromLyX);
213 // On Windows, we have to close and reopen
214 // the pipe after each use.
217 outPipeName().toLocal8Bit().constData(),
222 notifyLB->setText("An error occurred, "
230 if (errno == ECOMM) {
231 // When talking to a native Windows version of
232 // LyX, the second time we try to use the pipe,
233 // read() fails with ECOMM. In this case, we
234 // have to simply close and reopen it.
237 outPipeName().toLocal8Bit().constData(),
245 notifyLB->setText("An error occurred, closing pipes");
254 qWarning() << "monitor: Closing pipes";
256 notifyLB->setText("Closing pipes");
258 qWarning() << "monitor: LyX has closed connection!";
260 notifyLB->setText("LyX has closed connection!");
263 DeleteThread * event = new DeleteThread(pipethread);
264 QCoreApplication::postEvent(this, static_cast<QEvent *>(event));
270 bool LyXServerMonitor::event(QEvent * e)
272 if (e->type() == QEvent::User) {
273 ReadPipe * pipeThread =
274 static_cast<DeleteThread *>(e)->pipeThread();
280 return QDialog::event(e);
284 void LyXServerMonitor::openPipes()
287 qWarning() << "monitor: Opening pipes " << inPipeName()
288 << " and " << outPipeName();
289 pipein = ::open(inPipeName().toLocal8Bit().constData(),
291 pipeout = ::open(outPipeName().toLocal8Bit().constData(),
293 if (pipein < 0 || pipeout < 0) {
294 qWarning() << "monitor: Could not open the pipes";
296 notifyLB->setText("Could not open the pipes");
297 if (pipein >= 0 || pipeout >= 0)
301 pipethread = new ReadPipe(this);
303 if (!pipethread->isRunning()) {
304 qWarning() << "monitor: Could not create pipe thread";
306 notifyLB->setText("Could not create pipe thread");
310 openPipesPB->setDisabled(true);
311 closePipesPB->setDisabled(false);
313 QString const clientname = clientNameLE->text();
314 snprintf(buffer, BUFSIZE - 1,
315 "LYXSRV:%s:hello\n", clientname.toUtf8().constData());
316 buffer[BUFSIZE - 1] = '\0';
317 ::write(pipein, buffer, strlen(buffer));
319 qWarning() << "monitor: Pipes already opened, close them first\n";
323 void LyXServerMonitor::closePipes()
325 if (pipein == -1 && pipeout == -1) {
326 qWarning() << "monitor: Pipes are not opened";
333 QString const clientname = clientNameLE->text();
334 if (pipethread->isRunning()) {
336 // The thread, currently blocked on the read()
337 // call, will be waked up by the reply from
338 // LyX and will exit.
339 snprintf(buffer, BUFSIZE - 1,
340 "LYXCMD:%s:message:Client '%s' is leaving\n",
341 clientname.toUtf8().constData(),
342 clientname.toUtf8().constData());
343 buffer[BUFSIZE - 1] = '\0';
344 ::write(pipein, buffer, strlen(buffer));
347 snprintf(buffer, BUFSIZE - 1, "LYXSRV:%s:bye\n",
348 clientname.toUtf8().constData());
349 buffer[BUFSIZE - 1] = '\0';
350 ::write(pipein, buffer, strlen(buffer));
359 pipein = pipeout = -1;
360 submitCommandPB->setDisabled(true);
361 openPipesPB->setDisabled(false);
362 closePipesPB->setDisabled(true);
366 void LyXServerMonitor::submitCommand()
369 QString const command = commandLE->text();
370 QString const argument = argumentLE->text();
371 QString const clientname = clientNameLE->text();
372 snprintf(buffer, BUFSIZE - 2, "LYXCMD:%s:%s:%s",
373 clientname.toUtf8().constData(),
374 command.toUtf8().constData(),
375 argument.toUtf8().constData());
376 buffer[BUFSIZE - 1] = '\0';
377 qWarning() << "monitor: Sending: " << buffer;
378 strcat(buffer, "\n");
379 ::write(pipein, buffer, strlen(buffer));
381 qWarning() << "monitor: Pipe is not opened";
385 int main(int argc, char * argv[])
387 QApplication app(argc, argv);
388 LyXServerMonitor dialog;
389 return dialog.exec();
392 #include "moc_server_monitor.cpp"