3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Jean-Marc Lasgouttes
8 * \author Angus Leeming
10 * \author Enrico Forestieri
12 * Full author contact details are available in file CREDITS.
16 Docu : To use the lyxserver define the name of the pipe in your
18 \serverpipe "/home/myhome/.lyxpipe"
19 Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX.
20 Each message consists of a single line in ASCII. Input lines
21 (client -> LyX) have the following format:
22 "LYXCMD:<clientname>:<functionname>:<argument>"
23 Answers from LyX look like this:
24 "INFO:<clientname>:<functionname>:<data>"
25 [asierra970531] Or like this in case of error:
26 "ERROR:<clientname>:<functionname>:<error message>"
27 where <clientname> and <functionname> are just echoed.
28 If LyX notifies about a user defined extension key-sequence,
29 the line looks like this:
30 "NOTIFY:<key-sequence>"
31 [asierra970531] New server-only messages to implement a simple protocol
32 "LYXSRV:<clientname>:<protocol message>"
33 where <protocol message> can be "hello" or "bye". If hello is
34 received LyX will inform the client that it's listening its
35 messages, and 'bye' will inform that lyx is closing.
37 See development/server_monitor.c for an example client.
38 Purpose: implement a client/server lib for LyX
44 #include "FuncRequest.h"
45 #include "LyXAction.h"
48 #include "frontends/Application.h"
50 #include "support/debug.h"
51 #include "support/FileName.h"
52 #include "support/lassert.h"
53 #include "support/lstrings.h"
54 #include "support/os.h"
56 #include <boost/bind.hpp>
59 #include <QCoreApplication>
63 #ifdef HAVE_SYS_STAT_H
64 # include <sys/stat.h>
69 using namespace lyx::support;
70 using os::external_path;
74 /////////////////////////////////////////////////////////////////////
78 /////////////////////////////////////////////////////////////////////
82 class ReadReadyEvent : public QEvent {
85 ReadReadyEvent(DWORD inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
88 DWORD inpipe() const { return inpipe_; }
99 DWORD error = GetLastError();
100 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
101 FORMAT_MESSAGE_FROM_SYSTEM |
102 FORMAT_MESSAGE_IGNORE_INSERTS,
103 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
104 (LPTSTR) &msgbuf, 0, NULL);
105 return static_cast<char *>(msgbuf);
111 DWORD WINAPI pipeServerWrapper(void * arg)
113 LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
114 lyxcomm->pipeServer();
122 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
123 : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0)
130 void LyXComm::pipeServer()
134 for (i = 0; i <= MAX_PIPES; ++i) {
136 string const pipename = external_path(pipeName(i));
139 open_mode = PIPE_ACCESS_INBOUND;
142 open_mode = PIPE_ACCESS_OUTBOUND;
146 // Manual-reset event, initial state = signaled
147 event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
150 LYXERR0("LyXComm: Could not create event for pipe "
151 << pipename.c_str() << '\n' << errormsg());
156 pipe_[i].overlap.hEvent = event_[i];
157 pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
158 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
159 MAX_PIPES, PIPE_BUFSIZE, PIPE_BUFSIZE,
162 if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
163 LYXERR0("LyXComm: Could not create pipe "
164 << pipename.c_str() << '\n' << errormsg());
170 pipe_[i].state = pipe_[i].pending_io ?
171 CONNECTING_STATE : (i < MAX_PIPES ? READING_STATE
175 // Add the stopserver_ event
176 event_[MAX_PIPES + 1] = stopserver_;
179 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
184 while (!checkStopServer()) {
185 // Indefinitely wait for the completion of an overlapped
186 // read, write, or connect operation.
187 DWORD wait = WaitForMultipleObjects(MAX_PIPES + 2, event_,
190 // Determine which pipe instance completed the operation.
191 i = wait - WAIT_OBJECT_0;
192 LASSERT(i >= 0 && i <= MAX_PIPES + 1, /**/);
194 // Check whether we were waked up for stopping the pipe server.
195 if (i == MAX_PIPES + 1)
198 // Get the result if the operation was pending.
199 if (pipe_[i].pending_io) {
200 success = GetOverlappedResult(pipe_[i].handle,
201 &pipe_[i].overlap, &status, FALSE);
203 switch (pipe_[i].state) {
204 case CONNECTING_STATE:
205 // Pending connect operation
207 DWORD const err = GetLastError();
209 && err == ERROR_IO_INCOMPLETE) {
210 // A reply on the output pipe
211 // has not been read, still.
212 // As we have only one instance
213 // for output, we risk a stalled
214 // pipe if no one reads it.
215 // So, if a reader doesn't
216 // appear within about 5 or 6
217 // seconds, we reset it.
218 static int count = 0;
225 LYXERR0("LyXComm: " << errormsg());
228 pipe_[i].state = i < MAX_PIPES ? READING_STATE
233 // Pending read operation
234 LASSERT(i < MAX_PIPES, /**/);
235 if (!success || status == 0) {
239 pipe_[i].nbytes = status;
240 pipe_[i].state = WRITING_STATE;
244 // Pending write operation
245 LASSERT(i == MAX_PIPES, /**/);
246 if (!success || status != writebuf_.length()) {
254 // Operate according to the pipe state
255 switch (pipe_[i].state) {
257 // The pipe instance is connected to a client
258 // and is ready to read a request.
259 LASSERT(i < MAX_PIPES, /**/);
260 success = ReadFile(pipe_[i].handle,
261 pipe_[i].pipebuf, PIPE_BUFSIZE - 1,
262 &pipe_[i].nbytes, &pipe_[i].overlap);
264 if (success && pipe_[i].nbytes != 0) {
265 // The read operation completed successfully.
266 pipe_[i].pending_io = false;
267 pipe_[i].state = WRITING_STATE;
271 if (!success && GetLastError() == ERROR_IO_PENDING) {
272 // The read operation is still pending.
273 pipe_[i].pending_io = true;
277 // Client closed connection (ERROR_BROKEN_PIPE) or
278 // an error occurred; in either case, reset the pipe.
279 if (GetLastError() != ERROR_BROKEN_PIPE) {
280 LYXERR0("LyXComm: " << errormsg());
281 if (!readbuf_[i].empty()) {
282 LYXERR0("LyXComm: truncated command: "
293 // The request was successfully read
294 // from the client; commit it.
295 ReadReadyEvent * event = new ReadReadyEvent(i);
296 QCoreApplication::postEvent(this,
297 static_cast<QEvent *>(event));
298 // Wait for completion
299 while (pipe_[i].nbytes && !checkStopServer())
301 pipe_[i].pending_io = false;
302 pipe_[i].state = READING_STATE;
305 // Let's see whether we have a reply.
306 if (writebuf_.length() == 0) {
307 // No, nothing to do.
308 pipe_[i].pending_io = false;
312 success = WriteFile(pipe_[i].handle,
313 writebuf_.c_str(), writebuf_.length(),
314 &status, &pipe_[i].overlap);
316 if (success && status == writebuf_.length()) {
317 // The write operation completed successfully.
319 pipe_[i].pending_io = false;
324 if (!success && (GetLastError() == ERROR_IO_PENDING)) {
325 // The write operation is still pending.
326 pipe_[i].pending_io = true;
330 // Client closed connection (ERROR_NO_DATA) or
331 // an error occurred; in either case, reset the pipe.
332 if (GetLastError() != ERROR_NO_DATA) {
333 LYXERR0("LyXComm: Error sending message: "
334 << writebuf_ << '\n' << errormsg());
343 closeHandles(MAX_PIPES + 1);
347 void LyXComm::closeHandles(DWORD index)
349 for (int i = 0; i <= index; ++i) {
351 ResetEvent(event_[i]);
352 CloseHandle(event_[i]);
354 if (pipe_[i].handle != INVALID_HANDLE_VALUE)
355 CloseHandle(pipe_[i].handle);
360 bool LyXComm::event(QEvent * e)
362 if (e->type() == QEvent::User) {
363 read_ready(static_cast<ReadReadyEvent *>(e)->inpipe());
370 BOOL LyXComm::checkStopServer()
372 return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0;
376 void LyXComm::startPipe(DWORD index)
378 pipe_[index].pending_io = false;
380 // Overlapped ConnectNamedPipe should return zero.
381 if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
382 // FIXME: What to do? Maybe the pipe server should be reset.
383 LYXERR0("LyXComm: Could not connect pipe "
384 << external_path(pipeName(index)) << '\n'
389 switch (GetLastError()) {
390 case ERROR_IO_PENDING:
391 // The overlapped connection is in progress.
392 pipe_[index].pending_io = true;
395 case ERROR_PIPE_CONNECTED:
396 // Client is already connected, so signal an event.
397 if (SetEvent(pipe_[index].overlap.hEvent))
400 // Anything else is an error.
401 // FIXME: What to do? Maybe the pipe server should be reset.
402 LYXERR0("LyXComm: An error occurred while connecting pipe "
403 << external_path(pipeName(index)) << '\n'
409 void LyXComm::resetPipe(DWORD index, bool close_handle)
411 // This method is called when an error occurs or when a client
412 // closes the connection. We first disconnect the pipe instance,
413 // then reconnect it, ready to wait for another client.
415 if (!DisconnectNamedPipe(pipe_[index].handle)) {
416 LYXERR0("LyXComm: Could not disconnect pipe "
417 << external_path(pipeName(index)) << '\n'
419 // What to do now? Let's try whether re-creating the pipe helps.
423 DWORD const open_mode = index < MAX_PIPES ?
424 PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND;
425 string const name = external_path(pipeName(index));
427 CloseHandle(pipe_[index].handle);
429 pipe_[index].handle = CreateNamedPipe(name.c_str(),
430 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
431 MAX_PIPES, PIPE_BUFSIZE, PIPE_BUFSIZE,
434 if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
435 LYXERR0("LyXComm: Could not reset pipe "
436 << name << '\n' << errormsg());
439 if (index == MAX_PIPES)
442 readbuf_[index].erase();
445 pipe_[index].state = pipe_[index].pending_io ?
446 CONNECTING_STATE : (index < MAX_PIPES ? READING_STATE
451 void LyXComm::openConnection()
453 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
455 // If we are up, that's an error
457 LYXERR0("LyXComm: Already connected");
460 // We assume that we don't make it
463 if (pipename_.empty()) {
464 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
468 // Check whether the pipe name is being used by some other program.
469 if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
470 LYXERR0("LyXComm: Pipe " << external_path(inPipeName())
471 << " already exists.\nMaybe another instance of LyX"
477 // Manual-reset event, initial state = not signaled
478 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
479 server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
480 static_cast<void *>(this), 0, NULL);
481 if (!server_thread_) {
482 LYXERR0("LyXComm: Could not create pipe server thread\n"
491 void LyXComm::closeConnection()
493 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
495 if (pipename_.empty()) {
496 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
501 LYXERR(Debug::LYXSERVER, "LyXComm: Already disconnected");
505 SetEvent(stopserver_);
506 // Wait for the pipe server to finish
507 WaitForSingleObject(server_thread_, INFINITE);
508 CloseHandle(server_thread_);
509 ResetEvent(stopserver_);
510 CloseHandle(stopserver_);
514 void LyXComm::emergencyCleanup()
517 SetEvent(stopserver_);
518 // Forcibly terminate the pipe server thread if it does
519 // not finish quickly.
520 if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0)
521 TerminateThread(server_thread_, 0);
522 CloseHandle(server_thread_);
523 ResetEvent(stopserver_);
524 CloseHandle(stopserver_);
529 void LyXComm::read_ready(DWORD inpipe)
531 // Turn the pipe buffer into a C string
532 DWORD const nbytes = pipe_[inpipe].nbytes;
533 pipe_[inpipe].pipebuf[nbytes] = '\0';
535 readbuf_[inpipe] += rtrim(pipe_[inpipe].pipebuf, "\r");
537 // Commit any commands read
538 while (readbuf_[inpipe].find('\n') != string::npos) {
539 // split() grabs the entire string if
540 // the delim /wasn't/ found. ?:-P
542 readbuf_[inpipe] = split(readbuf_[inpipe], cmd, '\n');
543 cmd = rtrim(cmd, "\r");
544 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << nbytes
545 << ", readbuf_:" << readbuf_[inpipe]
548 clientcb_(client_, cmd);
551 // Signal that we are done.
552 pipe_[inpipe].nbytes = 0;
556 void LyXComm::send(string const & msg)
559 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
563 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
565 if (pipename_.empty())
569 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
573 // Wait a couple of secs for completion of a previous write operation.
574 for (int count = 0; writebuf_.length() && count < 20; ++count)
577 if (!writebuf_.length()) {
579 // Tell the pipe server he has a job to do.
580 SetEvent(pipe_[MAX_PIPES].overlap.hEvent);
582 // Nope, output pipe is still busy. Most likely, a bad client
583 // did not care to read the answer (JabRef is one such client).
584 // Let's do a reset, otherwise the output pipe could remain
585 // stalled if the pipe server failed to reset it.
586 // This will remedy the output pipe stall, but the client will
587 // get a broken pipe error.
588 LYXERR0("LyXComm: Error sending message: " << msg
589 << "\nLyXComm: Output pipe is stalled\n"
590 "LyXComm: Resetting connection");
592 if (!checkStopServer())
598 string const LyXComm::pipeName(DWORD index) const
600 return index < MAX_PIPES ? inPipeName() : outPipeName();
604 #elif !defined (HAVE_MKFIFO)
605 // We provide a stub class that disables the lyxserver.
607 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
610 void LyXComm::openConnection()
614 void LyXComm::closeConnection()
618 int LyXComm::startPipe(string const & filename, bool write)
624 void LyXComm::endPipe(int & fd, string const & filename, bool write)
628 void LyXComm::emergencyCleanup()
631 void LyXComm::read_ready()
635 void LyXComm::send(string const & msg)
639 #else // defined (HAVE_MKFIFO)
642 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
643 : pipename_(pip), client_(cli), clientcb_(ccb)
650 void LyXComm::openConnection()
652 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
654 // If we are up, that's an error
656 lyxerr << "LyXComm: Already connected" << endl;
659 // We assume that we don't make it
662 if (pipename_.empty()) {
663 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
667 infd_ = startPipe(inPipeName(), false);
671 outfd_ = startPipe(outPipeName(), true);
673 endPipe(infd_, inPipeName(), false);
677 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
678 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
679 << '\n' << strerror(errno) << endl;
685 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
690 void LyXComm::closeConnection()
692 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
694 if (pipename_.empty()) {
695 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
700 LYXERR0("LyXComm: Already disconnected");
704 endPipe(infd_, inPipeName(), false);
705 endPipe(outfd_, outPipeName(), true);
711 int LyXComm::startPipe(string const & file, bool write)
713 static bool stalepipe = false;
714 FileName const filename(file);
715 if (filename.exists()) {
717 // Let's see whether we have a stale pipe.
718 int fd = ::open(filename.toFilesystemEncoding().c_str(),
719 O_WRONLY | O_NONBLOCK);
721 // Another LyX instance is using it.
723 } else if (errno == ENXIO) {
724 // No process is reading from the other end.
726 LYXERR(Debug::LYXSERVER,
727 "LyXComm: trying to remove "
729 filename.removeFile();
731 } else if (stalepipe) {
732 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
734 filename.removeFile();
737 if (filename.exists()) {
738 lyxerr << "LyXComm: Pipe " << filename
739 << " already exists.\nIf no other LyX program"
740 " is active, please delete the pipe by hand"
748 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
749 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
750 << strerror(errno) << endl;
753 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
754 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
757 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
758 << strerror(errno) << endl;
759 filename.removeFile();
764 theApp()->registerSocketCallback(fd,
765 boost::bind(&LyXComm::read_ready, this));
772 void LyXComm::endPipe(int & fd, string const & filename, bool write)
778 theApp()->unregisterSocketCallback(fd);
780 if (::close(fd) < 0) {
781 lyxerr << "LyXComm: Could not close pipe " << filename
782 << '\n' << strerror(errno) << endl;
785 if (FileName(filename).removeFile() < 0) {
786 lyxerr << "LyXComm: Could not remove pipe " << filename
787 << '\n' << strerror(errno) << endl;
794 void LyXComm::emergencyCleanup()
796 if (!pipename_.empty()) {
797 endPipe(infd_, inPipeName(), false);
798 endPipe(outfd_, outPipeName(), true);
803 // Receives messages and sends then to client
804 void LyXComm::read_ready()
806 // FIXME: make read_buffer_ a class-member for multiple sessions
807 static string read_buffer_;
808 read_buffer_.erase();
810 int const charbuf_size = 100;
811 char charbuf[charbuf_size];
813 // As O_NONBLOCK is set, until no data is available for reading,
814 // read() doesn't block but returns -1 and set errno to EAGAIN.
815 // After a client that opened the pipe for writing, closes it
816 // (and no other client is using the pipe), read() would always
817 // return 0 and thus the connection has to be reset.
821 // the single = is intended here.
822 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
825 charbuf[status] = '\0'; // turn it into a c string
826 read_buffer_ += rtrim(charbuf, "\r");
827 // commit any commands read
828 while (read_buffer_.find('\n') != string::npos) {
829 // split() grabs the entire string if
830 // the delim /wasn't/ found. ?:-P
832 read_buffer_= split(read_buffer_, cmd,'\n');
833 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
834 << ", read_buffer_:" << read_buffer_
837 clientcb_(client_, cmd);
841 if (errno == EAGAIN) {
842 // Nothing to read, continue
846 // An error occurred, better bailing out
847 LYXERR0("LyXComm: " << strerror(errno));
848 if (!read_buffer_.empty()) {
849 LYXERR0("LyXComm: truncated command: " << read_buffer_);
850 read_buffer_.erase();
852 break; // reset connection
856 // The connection gets reset when read() returns 0 (meaning that the
857 // last client closed the pipe) or an error occurred, in which case
858 // read() returns -1 and errno != EAGAIN.
865 void LyXComm::send(string const & msg)
868 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
872 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
874 if (pipename_.empty()) return;
877 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
878 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
879 lyxerr << "LyXComm: Error sending message: " << msg
880 << '\n' << strerror(errno)
881 << "\nLyXComm: Resetting connection" << endl;
887 #endif // defined (HAVE_MKFIFO)
890 string const LyXComm::inPipeName() const
892 return pipename_ + ".in";
896 string const LyXComm::outPipeName() const
898 return pipename_ + ".out";
902 /////////////////////////////////////////////////////////////////////
906 /////////////////////////////////////////////////////////////////////
908 void ServerCallback(Server * server, string const & msg)
910 server->callback(msg);
913 Server::Server(LyXFunc * f, string const & pipes)
914 : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
920 // say goodbye to clients so they stop sending messages
921 // send as many bye messages as there are clients,
922 // each with client's name.
924 for (int i = 0; i != numclients_; ++i) {
925 message = "LYXSRV:" + clients_[i] + ":bye\n";
926 pipes_.send(message);
931 int compare(char const * a, char const * b, unsigned int len)
934 return strncmp(a, b, len);
938 // Handle data gotten from communication, called by LyXComm
939 void Server::callback(string const & msg)
941 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
943 char const * p = msg.c_str();
945 // --- parse the string --------------------------------------------
947 // Format: LYXCMD:<client>:<func>:<argstring>\n
949 bool server_only = false;
951 // --- 1. check 'header' ---
953 if (compare(p, "LYXSRV:", 7) == 0) {
955 } else if (0 != compare(p, "LYXCMD:", 7)) {
956 lyxerr << "Server: Unknown request \""
962 // --- 2. for the moment ignore the client name ---
964 while (*p && *p != ':')
965 client += char(*p++);
971 // --- 3. get function name ---
973 while (*p && *p != ':')
976 // --- 4. parse the argument ---
978 if (!server_only && *p == ':' && *(++p)) {
979 while (*p && *p != '\n')
984 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
985 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
987 // --- lookup and exec the command ------------------
991 // return the greeting to inform the client that
993 if (cmd == "hello") {
995 if (numclients_ == MAX_CLIENTS) { //paranoid check
996 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1000 while (!clients_[i].empty() && i < numclients_)
1002 clients_[i] = client;
1004 buf = "LYXSRV:" + client + ":hello\n";
1005 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1007 } else if (cmd == "bye") {
1008 // If clients_ == 0 maybe we should reset the pipes
1009 // to prevent fake callbacks
1010 int i = 0; //look if client is registered
1011 for (; i < numclients_; ++i) {
1012 if (clients_[i] == client)
1015 if (i < numclients_) {
1017 clients_[i].erase();
1018 LYXERR(Debug::LYXSERVER, "Server: Client "
1019 << client << " said goodbye");
1021 LYXERR(Debug::LYXSERVER,
1022 "Server: ignoring bye messge from unregistered client" << client);
1025 LYXERR0("Server: Undefined server command " << cmd << '.');
1031 // which lyxfunc should we let it connect to?
1032 // The correct solution would be to have a
1033 // specialized (non-gui) BufferView. But how do
1034 // we do it now? Probably we should just let it
1035 // connect to the lyxfunc in the single LyXView we
1036 // support currently. (Lgb)
1038 func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
1039 string const rval = to_utf8(func_->getMessage());
1041 // all commands produce an INFO or ERROR message
1042 // in the output pipe, even if they do not return
1043 // anything. See chapter 4 of Customization doc.
1045 if (func_->errorStat())
1049 buf += client + ':' + cmd + ':' + rval + '\n';
1052 // !!! we don't do any error checking -
1053 // if the client won't listen, the
1054 // message is lost and others too
1055 // maybe; so the client should empty
1056 // the outpipe before issuing a request.
1064 // Send a notify message to a client, called by WorkAreaKeyPress
1065 void Server::notifyClient(string const & s)
1067 pipes_.send("NOTIFY:" + s + "\n");
1074 #include "moc_Server.cpp"