]> git.lyx.org Git - lyx.git/blobdiff - src/Server.cpp
Fix crash when selecting text with changes
[lyx.git] / src / Server.cpp
index 2aed7fad371aa3ccdadb6d3fb7c3c0413f0adddc..566f8f334400b6594cf587f71f5de43016ff7326 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 "support/signals.h"
 
 
-#include <boost/bind.hpp>
+#include <iostream>
 
 #ifdef _WIN32
 
 #ifdef _WIN32
+#include <io.h>
 #include <QCoreApplication>
 #endif
 #include <QCoreApplication>
 #endif
+#include <QThread>
 
 #include <cerrno>
 #ifdef HAVE_SYS_STAT_H
 
 #include <cerrno>
 #ifdef HAVE_SYS_STAT_H
@@ -111,7 +117,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 +137,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();
 }
 
@@ -203,7 +210,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)
@@ -349,7 +356,7 @@ 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
                                // The write operation is still pending.
                                // We get here when a reader is started
                                // well before a reply is ready, so delay
@@ -415,6 +422,8 @@ bool LyXComm::checkStopServer(DWORD timeout)
 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)) {
@@ -511,8 +520,14 @@ void LyXComm::openConnection()
                return;
        }
 
                return;
        }
 
-       // Check whether the pipe name is being used by some other program.
+       // Check whether the pipe name is being used by some other instance.
        if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
        if (!stopserver_ && WaitNamedPipe(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;
@@ -681,6 +696,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()
 {}
 
@@ -702,6 +718,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()
 {}
 
@@ -712,11 +729,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();
 }
 
@@ -794,6 +811,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;
@@ -835,8 +858,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, [=](){
+                               if (!tracker.expired())
+                                       read_ready();
+                       });
        }
 
        return fd;
        }
 
        return fd;
@@ -856,7 +883,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;
        }
@@ -894,7 +921,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");
@@ -945,7 +971,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);
@@ -960,6 +987,49 @@ 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()
+{
+       int pipefd;
+       int loaded_files = 0;
+       FileName const pipe(inPipeName());
+       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
 {
@@ -984,8 +1054,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)
 {}
 
 
 {}
 
 
@@ -1004,7 +1074,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);
 }
 
@@ -1093,7 +1162,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 << '.');
@@ -1106,17 +1175,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:";