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`
28 * 1) Set the LyXserver pipe path in the LyX preferences (on *nix you can use
29 * any path, for example ~/.lyx/lyxpipe, whereas on Windows the path has
30 * to start with `\\.\pipe\', for example you can use \\.\pipe\lyxpipe).
31 * 2) Quit and restart LyX.
32 * 3) Launch this program, adjust the pipe name to match that one used in LyX,
33 * push the button labeled "Open pipes" and then try issuing some commands.
36 #include "server_monitor.h"
38 #include <QApplication>
42 class ReadPipe : public QThread {
45 ReadPipe(LyXServerMonitor * monitor) : lyxmonitor(monitor) {}
47 void run() { lyxmonitor->readPipe(); }
51 LyXServerMonitor * lyxmonitor;
55 class DeleteThread : public QEvent {
58 DeleteThread(ReadPipe * thread)
59 : QEvent(QEvent::User), pipethread(thread)
62 ReadPipe * pipeThread() const { return pipethread; }
66 ReadPipe * pipethread;
70 LyXServerMonitor::LyXServerMonitor()
71 : pipein(-1), pipeout(-1), thread_exit(false), lyx_listen(false)
76 char const * const home = getenv("HOME");
77 QString const pipeName = (home && home[0]) ?
78 QString::fromUtf8(home) + "/.lyx/lyxpipe" : "\\\\.\\pipe\\lyxpipe";
80 pipeNameLE->setText(pipeName);
81 clientNameLE->setText("monitor");
82 submitCommandPB->setDisabled(true);
83 closePipesPB->setDisabled(true);
85 connect(openPipesPB, SIGNAL(clicked()), this, SLOT(openPipes()));
86 connect(closePipesPB, SIGNAL(clicked()), this, SLOT(closePipes()));
87 connect(submitCommandPB, SIGNAL(clicked()), this, SLOT(submitCommand()));
88 connect(donePB, SIGNAL(clicked()), this, SLOT(reject()));
90 QVBoxLayout *mainLayout = new QVBoxLayout;
91 mainLayout->addWidget(gridGB);
92 mainLayout->addWidget(horizontalGB);
93 setLayout(mainLayout);
95 setWindowTitle("LyX Server Monitor");
99 LyXServerMonitor::~LyXServerMonitor()
106 void LyXServerMonitor::createGridGroupBox()
108 gridGB = new QGroupBox;
109 QGridLayout * layout = new QGridLayout;
111 labels[0] = new QLabel("Pipe name");
112 pipeNameLE = new QLineEdit;
113 layout->addWidget(labels[0], 0, 0, Qt::AlignRight);
114 layout->addWidget(pipeNameLE, 0, 1);
116 labels[1] = new QLabel("Command");
117 commandLE = new QLineEdit;
118 layout->addWidget(labels[1], 1, 0, Qt::AlignRight);
119 layout->addWidget(commandLE, 1, 1);
121 labels[2] = new QLabel("Client name");
122 clientNameLE = new QLineEdit;
123 layout->addWidget(labels[2], 0, 2, Qt::AlignRight);
124 layout->addWidget(clientNameLE, 0, 3);
126 labels[3] = new QLabel("Argument");
127 argumentLE = new QLineEdit;
128 layout->addWidget(labels[3], 1, 2, Qt::AlignRight);
129 layout->addWidget(argumentLE, 1, 3);
131 labels[4] = new QLabel("Info");
133 infoLB->setFrameStyle(QFrame::Panel | QFrame::Sunken);
134 infoLB->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
135 layout->addWidget(labels[4], 2, 0, Qt::AlignRight);
136 layout->addWidget(infoLB, 2, 1, 1, 3);
138 labels[5] = new QLabel("Notify");
139 notifyLB = new QLabel;
140 notifyLB->setFrameStyle(QFrame::Panel | QFrame::Sunken);
141 notifyLB->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
142 layout->addWidget(labels[5], 3, 0, Qt::AlignRight);
143 layout->addWidget(notifyLB, 3, 1, 1, 3);
145 layout->setColumnMinimumWidth(1, 200);
146 layout->setColumnMinimumWidth(3, 200);
147 gridGB->setLayout(layout);
151 void LyXServerMonitor::createCmdsGroupBox()
153 horizontalGB = new QGroupBox;
154 QHBoxLayout * layout = new QHBoxLayout;
156 openPipesPB = new QPushButton("&Open pipes");
157 layout->addWidget(openPipesPB);
159 closePipesPB = new QPushButton("C&lose pipes");
160 layout->addWidget(closePipesPB);
162 submitCommandPB = new QPushButton("&Submit Command");
163 layout->addWidget(submitCommandPB);
165 donePB = new QPushButton("&Done");
166 layout->addWidget(donePB);
168 horizontalGB->setLayout(layout);
172 void LyXServerMonitor::readPipe()
176 bool notified = false;
178 while ((n = ::read(pipeout, pipedata, BUFSIZE - 1)) && !thread_exit) {
181 QString const fromLyX =
182 QString::fromUtf8(pipedata).trimmed();
183 qWarning() << "monitor: Coming: " << fromLyX;
184 if (fromLyX.startsWith("LYXSRV:")) {
185 if (fromLyX.contains("bye")) {
186 qWarning() << "monitor: LyX has closed "
189 notifyLB->setText(fromLyX);
193 if (fromLyX.contains("hello")) {
195 qWarning() << "monitor: "
197 submitCommandPB->setDisabled(false);
200 if (fromLyX[0] == QLatin1Char('I')) {
201 infoLB->setText(fromLyX);
205 notifyLB->setText(fromLyX);
208 // On Windows, we have to close and reopen
209 // the pipe after each use.
212 outPipeName().toLocal8Bit().constData(),
217 notifyLB->setText("An error occurred, "
225 if (errno == ECOMM) {
226 // When talking to a native Windows version of
227 // LyX, the second time we try to use the pipe,
228 // read() fails with ECOMM. In this case, we
229 // have to simply close and reopen it.
232 outPipeName().toLocal8Bit().constData(),
240 notifyLB->setText("An error occurred, closing pipes");
249 qWarning() << "monitor: Closing pipes";
251 notifyLB->setText("Closing pipes");
253 qWarning() << "monitor: LyX has closed connection!";
255 notifyLB->setText("LyX has closed connection!");
258 DeleteThread * event = new DeleteThread(pipethread);
259 QCoreApplication::postEvent(this, static_cast<QEvent *>(event));
265 bool LyXServerMonitor::event(QEvent * e)
267 if (e->type() == QEvent::User) {
268 ReadPipe * pipeThread =
269 static_cast<DeleteThread *>(e)->pipeThread();
275 return QDialog::event(e);
279 void LyXServerMonitor::openPipes()
282 qWarning() << "monitor: Opening pipes " << inPipeName()
283 << " and " << outPipeName();
284 pipein = ::open(inPipeName().toLocal8Bit().constData(),
286 pipeout = ::open(outPipeName().toLocal8Bit().constData(),
288 if (pipein < 0 || pipeout < 0) {
289 qWarning() << "monitor: Could not open the pipes";
291 notifyLB->setText("Could not open the pipes");
292 if (pipein >= 0 || pipeout >= 0)
296 pipethread = new ReadPipe(this);
298 if (!pipethread->isRunning()) {
299 qWarning() << "monitor: Could not create pipe thread";
301 notifyLB->setText("Could not create pipe thread");
305 openPipesPB->setDisabled(true);
306 closePipesPB->setDisabled(false);
308 QString const clientname = clientNameLE->text();
309 snprintf(buffer, BUFSIZE - 1,
310 "LYXSRV:%s:hello\n", clientname.toUtf8().constData());
311 buffer[BUFSIZE - 1] = '\0';
312 ::write(pipein, buffer, strlen(buffer));
314 qWarning() << "monitor: Pipes already opened, close them first\n";
318 void LyXServerMonitor::closePipes()
320 if (pipein == -1 && pipeout == -1) {
321 qWarning() << "monitor: Pipes are not opened";
328 QString const clientname = clientNameLE->text();
329 if (pipethread->isRunning()) {
331 // The thread, currently blocked on the read()
332 // call, will be waked up by the reply from
333 // LyX and will exit.
334 snprintf(buffer, BUFSIZE - 1,
335 "LYXCMD:%s:message:Client '%s' is leaving\n",
336 clientname.toUtf8().constData(),
337 clientname.toUtf8().constData());
338 buffer[BUFSIZE - 1] = '\0';
339 ::write(pipein, buffer, strlen(buffer));
342 snprintf(buffer, BUFSIZE - 1, "LYXSRV:%s:bye\n",
343 clientname.toUtf8().constData());
344 buffer[BUFSIZE - 1] = '\0';
345 ::write(pipein, buffer, strlen(buffer));
354 pipein = pipeout = -1;
355 submitCommandPB->setDisabled(true);
356 openPipesPB->setDisabled(false);
357 closePipesPB->setDisabled(true);
361 void LyXServerMonitor::submitCommand()
364 QString const command = commandLE->text();
365 QString const argument = argumentLE->text();
366 QString const clientname = clientNameLE->text();
367 snprintf(buffer, BUFSIZE - 2, "LYXCMD:%s:%s:%s",
368 clientname.toUtf8().constData(),
369 command.toUtf8().constData(),
370 argument.toUtf8().constData());
371 buffer[BUFSIZE - 1] = '\0';
372 qWarning() << "monitor: Sending: " << buffer;
373 strcat(buffer, "\n");
374 ::write(pipein, buffer, strlen(buffer));
376 qWarning() << "monitor: Pipe is not opened";
380 int main(int argc, char *argv[])
382 QApplication app(argc, argv);
383 LyXServerMonitor dialog;
384 return dialog.exec();
387 #include "moc_server_monitor.cpp"