]> git.lyx.org Git - features.git/blobdiff - src/Server.cpp
Load ucs before utf8x inputenc with recent ucs versions
[features.git] / src / Server.cpp
index 08867d837c354d781fe9cbe55f0b205414e0f052..522060e91b6a70682b7ad0163ae9449cafbb240e 100644 (file)
           received LyX will inform the client that it's listening its
           messages, and 'bye' will inform that lyx is closing.
 
           received LyX will inform the client that it's listening its
           messages, and 'bye' will inform that lyx is closing.
 
-          See development/server_monitor.c for an example client.
+          See development/lyxserver/server_monitor.cpp for an example client.
   Purpose: implement a client/server lib for LyX
 */
 
 #include <config.h>
 
 #include "Server.h"
   Purpose: implement a client/server lib for LyX
 */
 
 #include <config.h>
 
 #include "Server.h"
+
+#include "DispatchResult.h"
 #include "FuncRequest.h"
 #include "FuncRequest.h"
+#include "LyX.h"
 #include "LyXAction.h"
 #include "LyXAction.h"
-#include "LyXFunc.h"
 
 #include "frontends/Application.h"
 
 #include "support/debug.h"
 #include "support/FileName.h"
 
 #include "frontends/Application.h"
 
 #include "support/debug.h"
 #include "support/FileName.h"
+#include "support/filetools.h"
 #include "support/lassert.h"
 #include "support/lstrings.h"
 #include "support/os.h"
 
 #include "support/lassert.h"
 #include "support/lstrings.h"
 #include "support/os.h"
 
-#include <boost/bind.hpp>
+#include <iostream>
 
 #ifdef _WIN32
 
 #ifdef _WIN32
-#include <QCoreApplication>
+# include <io.h>
+# include <QCoreApplication>
+#else
+# ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+# endif
 #endif
 #endif
+#include <QThread>
 
 #include <cerrno>
 #ifdef HAVE_SYS_STAT_H
 
 #include <cerrno>
 #ifdef HAVE_SYS_STAT_H
@@ -111,7 +120,7 @@ string errormsg(DWORD const error)
        return message;
 }
 
        return message;
 }
 
-} // namespace anon
+} // namespace
 
 
 DWORD WINAPI pipeServerWrapper(void * arg)
 
 
 DWORD WINAPI pipeServerWrapper(void * arg)
@@ -131,13 +140,14 @@ DWORD WINAPI pipeServerWrapper(void * arg)
 
 
 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
 
 
 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
-       : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0)
+       : stopserver_(0),
+         ready_(false), pipename_(pip), client_(cli), clientcb_(ccb),
+         deferred_loading_(false)
 {
        for (int i = 0; i < MAX_PIPES; ++i) {
                event_[i] = 0;
                pipe_[i].handle = INVALID_HANDLE_VALUE;
        }
 {
        for (int i = 0; i < MAX_PIPES; ++i) {
                event_[i] = 0;
                pipe_[i].handle = INVALID_HANDLE_VALUE;
        }
-       ready_ = false;
        openConnection();
 }
 
        openConnection();
 }
 
@@ -165,7 +175,7 @@ bool LyXComm::pipeServer()
 
                pipe_[i].overlap.hEvent = event_[i];
                pipe_[i].iobuf.erase();
 
                pipe_[i].overlap.hEvent = event_[i];
                pipe_[i].iobuf.erase();
-               pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
+               pipe_[i].handle = CreateNamedPipeA(pipename.c_str(),
                                open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
                                MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
                                PIPE_TIMEOUT, NULL);
                                open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
                                MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
                                PIPE_TIMEOUT, NULL);
@@ -192,8 +202,8 @@ bool LyXComm::pipeServer()
        LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
        ready_ = true;
        outbuf_.erase();
        LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
        ready_ = true;
        outbuf_.erase();
-       DWORD status;
-       bool success;
+       DWORD status = 0;
+       bool success = false;
 
        while (!checkStopServer()) {
                // Indefinitely wait for the completion of an overlapped
 
        while (!checkStopServer()) {
                // Indefinitely wait for the completion of an overlapped
@@ -203,7 +213,7 @@ bool LyXComm::pipeServer()
 
                // Determine which pipe instance completed the operation.
                i = wait - WAIT_OBJECT_0;
 
                // Determine which pipe instance completed the operation.
                i = wait - WAIT_OBJECT_0;
-               LASSERT(i >= 0 && i <= MAX_PIPES, /**/);
+               LASSERT(i <= MAX_PIPES, /**/);
 
                // Check whether we were waked up for stopping the pipe server.
                if (i == MAX_PIPES)
 
                // Check whether we were waked up for stopping the pipe server.
                if (i == MAX_PIPES)
@@ -319,8 +329,8 @@ bool LyXComm::pipeServer()
                                QCoreApplication::postEvent(this,
                                                static_cast<QEvent *>(event));
                                // Wait for completion
                                QCoreApplication::postEvent(this,
                                                static_cast<QEvent *>(event));
                                // Wait for completion
-                               while (pipe_[i].nbytes && !checkStopServer())
-                                       Sleep(100);
+                               while (pipe_[i].nbytes && !checkStopServer(100))
+                                       ;
                                pipe_[i].pending_io = false;
                                pipe_[i].state = READING_STATE;
                                continue;
                                pipe_[i].pending_io = false;
                                pipe_[i].state = READING_STATE;
                                continue;
@@ -349,12 +359,12 @@ bool LyXComm::pipeServer()
 
                        error = GetLastError();
 
 
                        error = GetLastError();
 
-                       if (success && error == ERROR_IO_PENDING) {
+                       if (success && (error == ERROR_IO_PENDING || error == NO_ERROR)) {
                                // The write operation is still pending.
                                // We get here when a reader is started
                                // well before a reply is ready, so delay
                                // a bit in order to not burden the cpu.
                                // The write operation is still pending.
                                // We get here when a reader is started
                                // well before a reply is ready, so delay
                                // a bit in order to not burden the cpu.
-                               Sleep(100);
+                               checkStopServer(100);
                                pipe_[i].pending_io = true;
                                continue;
                        }
                                pipe_[i].pending_io = true;
                                continue;
                        }
@@ -371,6 +381,9 @@ bool LyXComm::pipeServer()
                        if (!resetPipe(i, !success))
                                return false;
                        break;
                        if (!resetPipe(i, !success))
                                return false;
                        break;
+               case CONNECTING_STATE:
+                       LYXERR0("Wrong pipe state");
+                       break;
                }
        }
 
                }
        }
 
@@ -406,15 +419,17 @@ bool LyXComm::event(QEvent * e)
 }
 
 
 }
 
 
-bool LyXComm::checkStopServer()
+bool LyXComm::checkStopServer(DWORD timeout)
 {
 {
-       return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0;
+       return WaitForSingleObject(stopserver_, timeout) == WAIT_OBJECT_0;
 }
 
 
 bool LyXComm::startPipe(DWORD index)
 {
        pipe_[index].pending_io = false;
 }
 
 
 bool LyXComm::startPipe(DWORD index)
 {
        pipe_[index].pending_io = false;
+       pipe_[index].overlap.Offset = 0;
+       pipe_[index].overlap.OffsetHigh = 0;
 
        // Overlapped ConnectNamedPipe should return zero.
        if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
 
        // Overlapped ConnectNamedPipe should return zero.
        if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
@@ -474,7 +489,7 @@ bool LyXComm::resetPipe(DWORD index, bool close_handle)
                CloseHandle(pipe_[index].handle);
 
                pipe_[index].iobuf.erase();
                CloseHandle(pipe_[index].handle);
 
                pipe_[index].iobuf.erase();
-               pipe_[index].handle = CreateNamedPipe(name.c_str(),
+               pipe_[index].handle = CreateNamedPipeA(name.c_str(),
                                open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
                                MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
                                PIPE_TIMEOUT, NULL);
                                open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
                                MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
                                PIPE_TIMEOUT, NULL);
@@ -511,8 +526,14 @@ void LyXComm::openConnection()
                return;
        }
 
                return;
        }
 
-       // Check whether the pipe name is being used by some other program.
-       if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
+       // Check whether the pipe name is being used by some other instance.
+       if (!stopserver_ && WaitNamedPipeA(inPipeName().c_str(), 0)) {
+               // Tell the running instance to load the files
+               if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
+                       deferred_loading_ = true;
+                       pipename_.erase();
+                       return;
+               }
                lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
                       << " already exists.\nMaybe another instance of LyX"
                          " is using it." << endl;
                lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
                       << " already exists.\nMaybe another instance of LyX"
                          " is using it." << endl;
@@ -664,8 +685,7 @@ void LyXComm::send(string const & msg)
                       << "\nLyXComm: Resetting connection" << endl;
                ReleaseMutex(outbuf_mutex_);
                closeConnection();
                       << "\nLyXComm: Resetting connection" << endl;
                ReleaseMutex(outbuf_mutex_);
                closeConnection();
-               if (!checkStopServer())
-                       openConnection();
+               openConnection();
        }
 }
 
        }
 }
 
@@ -682,6 +702,7 @@ string const LyXComm::pipeName(DWORD index) const
 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
 {}
 
 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
 {}
 
+
 void LyXComm::openConnection()
 {}
 
 void LyXComm::openConnection()
 {}
 
@@ -703,6 +724,7 @@ void LyXComm::endPipe(int & fd, string const & filename, bool write)
 void LyXComm::emergencyCleanup()
 {}
 
 void LyXComm::emergencyCleanup()
 {}
 
+
 void LyXComm::read_ready()
 {}
 
 void LyXComm::read_ready()
 {}
 
@@ -713,11 +735,11 @@ void LyXComm::send(string const & msg)
 
 #else // defined (HAVE_MKFIFO)
 
 
 #else // defined (HAVE_MKFIFO)
 
-
 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
-       : pipename_(pip), client_(cli), clientcb_(ccb)
+       : infd_(-1), outfd_(-1),
+         ready_(false), pipename_(pip), client_(cli), clientcb_(ccb),
+         deferred_loading_(false)
 {
 {
-       ready_ = false;
        openConnection();
 }
 
        openConnection();
 }
 
@@ -795,6 +817,12 @@ int LyXComm::startPipe(string const & file, bool write)
                        if (fd >= 0) {
                                // Another LyX instance is using it.
                                ::close(fd);
                        if (fd >= 0) {
                                // Another LyX instance is using it.
                                ::close(fd);
+                               // Tell the running instance to load the files
+                               if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
+                                       deferred_loading_ = true;
+                                       pipename_.erase();
+                                       return -1;
+                               }
                        } else if (errno == ENXIO) {
                                // No process is reading from the other end.
                                stalepipe = true;
                        } else if (errno == ENXIO) {
                                // No process is reading from the other end.
                                stalepipe = true;
@@ -836,8 +864,12 @@ int LyXComm::startPipe(string const & file, bool write)
        }
 
        if (!write) {
        }
 
        if (!write) {
-               theApp()->registerSocketCallback(fd,
-                       boost::bind(&LyXComm::read_ready, this));
+               // Make sure not to call read_ready after destruction.
+               weak_ptr<void> tracker = tracker_.p();
+               theApp()->registerSocketCallback(fd, [this, tracker](){
+                               if (!tracker.expired())
+                                       read_ready();
+                       });
        }
 
        return fd;
        }
 
        return fd;
@@ -857,7 +889,7 @@ void LyXComm::endPipe(int & fd, string const & filename, bool write)
                       << '\n' << strerror(errno) << endl;
        }
 
                       << '\n' << strerror(errno) << endl;
        }
 
-       if (FileName(filename).removeFile() < 0) {
+       if (!FileName(filename).removeFile()) {
                lyxerr << "LyXComm: Could not remove pipe " << filename
                       << '\n' << strerror(errno) << endl;
        }
                lyxerr << "LyXComm: Could not remove pipe " << filename
                       << '\n' << strerror(errno) << endl;
        }
@@ -895,7 +927,6 @@ void LyXComm::read_ready()
        int status;
        // the single = is intended here.
        while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
        int status;
        // the single = is intended here.
        while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
-
                if (status > 0) {
                        charbuf[status] = '\0'; // turn it into a c string
                        read_buffer_ += rtrim(charbuf, "\r");
                if (status > 0) {
                        charbuf[status] = '\0'; // turn it into a c string
                        read_buffer_ += rtrim(charbuf, "\r");
@@ -946,7 +977,8 @@ void LyXComm::send(string const & msg)
 
        LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
 
 
        LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
 
-       if (pipename_.empty()) return;
+       if (pipename_.empty())
+               return;
 
        if (!ready_) {
                LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
 
        if (!ready_) {
                LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
@@ -961,6 +993,65 @@ void LyXComm::send(string const & msg)
 
 #endif // defined (HAVE_MKFIFO)
 
 
 #endif // defined (HAVE_MKFIFO)
 
+namespace {
+
+struct Sleep : QThread
+{
+       static void millisec(unsigned long ms)
+       {
+               QThread::usleep(ms * 1000);
+       }
+};
+
+} // namespace
+
+
+bool LyXComm::loadFilesInOtherInstance() const
+{
+       int pipefd;
+       FileName const pipe(inPipeName());
+
+       if (theFilesToLoad().empty()) {
+               LYXERR0("LyX is already running in another instance\n"
+                       "and 'use single instance' is active.");
+               // Wait a while for the other instance to reset the connection
+               Sleep::millisec(200);
+               pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
+               if (pipefd >= 0) {
+                       string const cmd = "LYXCMD:pipe:window-raise\n";
+                       if (::write(pipefd, cmd.c_str(), cmd.length()) < 0)
+                               LYXERR0("Cannot communicate with running instance!");
+                       ::close(pipefd);
+               }
+               return true;
+       }
+
+       int loaded_files = 0;
+       vector<string>::iterator it = theFilesToLoad().begin();
+       while (it != theFilesToLoad().end()) {
+               FileName fname = fileSearch(string(), os::internal_path(*it),
+                                               "lyx", may_not_exist);
+               if (fname.empty()) {
+                       ++it;
+                       continue;
+               }
+               // Wait a while to allow time for the other
+               // instance to reset the connection
+               Sleep::millisec(200);
+               pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
+               if (pipefd < 0)
+                       break;
+               string const cmd = "LYXCMD:pipe:file-open:" +
+                                       fname.absFileName() + '\n';
+               if (::write(pipefd, cmd.c_str(), cmd.length()) < 0)
+                       LYXERR0("Cannot write to pipe!");
+               ::close(pipefd);
+               ++loaded_files;
+               it = theFilesToLoad().erase(it);
+       }
+       return loaded_files > 0;
+}
+
 
 string const LyXComm::inPipeName() const
 {
 
 string const LyXComm::inPipeName() const
 {
@@ -985,8 +1076,8 @@ void ServerCallback(Server * server, string const & msg)
        server->callback(msg);
 }
 
        server->callback(msg);
 }
 
-Server::Server(LyXFunc * f, string const & pipes)
-       : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
+Server::Server(string const & pipes)
+       : numclients_(0), pipes_(pipes, this, &ServerCallback)
 {}
 
 
 {}
 
 
@@ -1005,7 +1096,6 @@ Server::~Server()
 
 int compare(char const * a, char const * b, unsigned int len)
 {
 
 int compare(char const * a, char const * b, unsigned int len)
 {
-       using namespace std;
        return strncmp(a, b, len);
 }
 
        return strncmp(a, b, len);
 }
 
@@ -1094,7 +1184,7 @@ void Server::callback(string const & msg)
                                                << client << " said goodbye");
                                } else {
                                        LYXERR(Debug::LYXSERVER,
                                                << client << " said goodbye");
                                } else {
                                        LYXERR(Debug::LYXSERVER,
-                                               "Server: ignoring bye messge from unregistered client" << client);
+                                               "Server: ignoring bye message from unregistered client" << client);
                                }
                        } else {
                                LYXERR0("Server: Undefined server command " << cmd << '.');
                                }
                        } else {
                                LYXERR0("Server: Undefined server command " << cmd << '.');
@@ -1107,17 +1197,20 @@ void Server::callback(string const & msg)
                        // The correct solution would be to have a
                        // specialized (non-gui) BufferView. But how do
                        // we do it now? Probably we should just let it
                        // The correct solution would be to have a
                        // specialized (non-gui) BufferView. But how do
                        // we do it now? Probably we should just let it
-                       // connect to the lyxfunc in the single LyXView we
+                       // connect to the lyxfunc in the single GuiView we
                        // support currently. (Lgb)
 
                        // support currently. (Lgb)
 
-                       func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
-                       string const rval = to_utf8(func_->getMessage());
+                       FuncRequest fr(lyxaction.lookupFunc(cmd), from_utf8(arg));
+                       fr.setOrigin(FuncRequest::LYXSERVER);
+                       DispatchResult dr;
+                       theApp()->dispatch(fr, dr);
+                       string const rval = to_utf8(dr.message());
 
                        // all commands produce an INFO or ERROR message
                        // in the output pipe, even if they do not return
                        // anything. See chapter 4 of Customization doc.
                        string buf;
 
                        // all commands produce an INFO or ERROR message
                        // in the output pipe, even if they do not return
                        // anything. See chapter 4 of Customization doc.
                        string buf;
-                       if (func_->errorStat())
+                       if (dr.error())
                                buf = "ERROR:";
                        else
                                buf = "INFO:";
                                buf = "ERROR:";
                        else
                                buf = "INFO:";