On Fri, Sep 11, 2009 at 01:14:39AM +0200, Vincent van Ravesteijn wrote: > > > >> You don't need Cygwin to test it. > >> > >> 1. Use "\\.\pipe\lyxpipe" (without quotes) as the LyXserver pipe path > >> in the preferences. > >> 2. Quit and restart LyX. > >> 3. In a cmd.exe terminal type > >> echo LYXCMD:test:file-open> \\.\pipe\lyxpipe.in > >> type \\.\pipe\lyxpipe.out > >> > >> If the file dialog opens, it works. > > This doesn't work. I get an error message: "LyXComm: The parameter is > > invalid", or ERROR_INVALID_PARAMETER in server....@294. > > > > If I do the following it works (but it'll not be the correct fix, but > > you know at least that it works in this case): > > > > server....@280--282: > > success = ReadFile(pipe_[i].handle, > > pipe_[i].readbuf, PIPE_BUFSIZE - 1, > > + &pipe_[i].nbytes, &pipe_[i].overlap); > > - &pipe_[i].nbytes, NULL); > > > > The following fixes it for me: > > bool LyXComm::startPipe(DWORD index) > { > pipe_[index].pending_io = false; > > + pipe_[index].overlap.Offset = 0; > + pipe_[index].overlap.OffsetHigh = 0; > > > see http://msdn.microsoft.com/en-us/library/aa365603(VS.85).aspx and > scroll down to the third comment:
I was actually following that example but somehow missed the comment :( I wonder why it was working for me. Could it be a gcc vs msvc issue? Thanks for spotting. Updated patch attached. -- Enrico
Index: src/Makefile.am =================================================================== --- src/Makefile.am (revision 31369) +++ src/Makefile.am (working copy) @@ -5,6 +5,7 @@ include $(top_srcdir)/config/common.am DISTCLEANFILES += config.h libintl.h AM_CPPFLAGS += $(PCH_FLAGS) -I$(top_srcdir)/src $(BOOST_INCLUDES) +AM_CPPFLAGS += $(QT4_CPPFLAGS) $(QT4_CORE_INCLUDES) if BUILD_CLIENT_SUBDIR CLIENT = client @@ -289,6 +290,22 @@ liblyxcore_la_SOURCES = $(SOURCEFILESCOR endif +if INSTALL_WINDOWS + +MOCHEADER = Server.h + +MOCEDFILES = $(MOCHEADER:%.h=%_moc.cpp) + +BUILT_SOURCES += $(MOCEDFILES) +CLEANFILES += $(MOCEDFILES) + +%_moc.cpp: %.h + $(MOC4) -D_WIN32 -o $@ $< + +liblyxcore_la_DEPENDENCIES = $(MOCEDFILES) + +endif + ############################### Graphics ############################## noinst_LTLIBRARIES += liblyxgraphics.la Index: src/LyXFunc.cpp =================================================================== --- src/LyXFunc.cpp (revision 31369) +++ src/LyXFunc.cpp (working copy) @@ -1168,6 +1168,7 @@ void LyXFunc::dispatch(FuncRequest const int row; istringstream is(argument); is >> file_name >> row; + file_name = os::internal_path(file_name); Buffer * buf = 0; bool loaded = false; string const abstmp = package().temp_dir().absFilename(); Index: src/Server.cpp =================================================================== --- src/Server.cpp (revision 31369) +++ src/Server.cpp (working copy) @@ -7,6 +7,7 @@ * \author Jean-Marc Lasgouttes * \author Angus Leeming * \author John Levon + * \author Enrico Forestieri * * Full author contact details are available in file CREDITS. */ @@ -48,10 +49,16 @@ #include "support/debug.h" #include "support/FileName.h" +#include "support/lassert.h" #include "support/lstrings.h" +#include "support/os.h" #include <boost/bind.hpp> +#ifdef _WIN32 +#include <QCoreApplication> +#endif + #include <cerrno> #ifdef HAVE_SYS_STAT_H # include <sys/stat.h> @@ -60,6 +67,7 @@ using namespace std; using namespace lyx::support; +using os::external_path; namespace lyx { @@ -69,7 +77,607 @@ namespace lyx { // ///////////////////////////////////////////////////////////////////// -#if !defined (HAVE_MKFIFO) +#if defined(_WIN32) + +class ReadReadyEvent : public QEvent { +public: + /// + ReadReadyEvent(DWORD inpipe) : QEvent(QEvent::User), inpipe_(inpipe) + {} + /// + DWORD inpipe() const { return inpipe_; } + +private: + DWORD inpipe_; +}; + +namespace { + +string errormsg(DWORD const error) +{ + void * msgbuf; + string message; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &msgbuf, 0, NULL)) { + message = static_cast<char *>(msgbuf); + LocalFree(msgbuf); + } else + message = "Unknown error"; + + return message; +} + +} // namespace anon + + +DWORD WINAPI pipeServerWrapper(void * arg) +{ + LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg); + if (!lyxcomm->pipeServer()) { + // Error exit; perform cleanup. + lyxcomm->ready_ = false; + lyxcomm->closeHandles(); + CloseHandle(lyxcomm->server_thread_); + CloseHandle(lyxcomm->stopserver_); + CloseHandle(lyxcomm->outbuf_mutex_); + lyxerr << "LyXComm: Closing connection" << endl; + } + return 1; +} + + +LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb) + : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0) +{ + for (int i = 0; i < MAX_PIPES; ++i) { + event_[i] = 0; + pipe_[i].handle = INVALID_HANDLE_VALUE; + } + ready_ = false; + openConnection(); +} + + +bool LyXComm::pipeServer() +{ + DWORD i; + DWORD error; + + for (i = 0; i < MAX_PIPES; ++i) { + bool const is_outpipe = i >= MAX_CLIENTS; + DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND + : PIPE_ACCESS_INBOUND; + string const pipename = external_path(pipeName(i)); + + // Manual-reset event, initial state = signaled + event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL); + if (!event_[i]) { + error = GetLastError(); + lyxerr << "LyXComm: Could not create event for pipe " + << pipename << "\nLyXComm: " + << errormsg(error) << endl; + return false; + } + + pipe_[i].overlap.hEvent = event_[i]; + pipe_[i].iobuf.erase(); + pipe_[i].handle = CreateNamedPipe(pipename.c_str(), + open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT, + MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE, + PIPE_TIMEOUT, NULL); + + if (pipe_[i].handle == INVALID_HANDLE_VALUE) { + error = GetLastError(); + lyxerr << "LyXComm: Could not create pipe " + << pipename << "\nLyXComm: " + << errormsg(error) << endl; + return false; + } + + if (!startPipe(i)) + return false; + pipe_[i].state = pipe_[i].pending_io ? + CONNECTING_STATE : (is_outpipe ? WRITING_STATE + : READING_STATE); + } + + // Add the stopserver_ event + event_[MAX_PIPES] = stopserver_; + + // We made it! + LYXERR(Debug::LYXSERVER, "LyXComm: Connection established"); + ready_ = true; + outbuf_.erase(); + DWORD status; + bool success; + + while (!checkStopServer()) { + // Indefinitely wait for the completion of an overlapped + // read, write, or connect operation. + DWORD wait = WaitForMultipleObjects(MAX_PIPES + 1, event_, + FALSE, INFINITE); + + // Determine which pipe instance completed the operation. + i = wait - WAIT_OBJECT_0; + LASSERT(i >= 0 && i <= MAX_PIPES, /**/); + + // Check whether we were waked up for stopping the pipe server. + if (i == MAX_PIPES) + break; + + bool const is_outpipe = i >= MAX_CLIENTS; + + // Get the result if the operation was pending. + if (pipe_[i].pending_io) { + success = GetOverlappedResult(pipe_[i].handle, + &pipe_[i].overlap, &status, FALSE); + + switch (pipe_[i].state) { + case CONNECTING_STATE: + // Pending connect operation + if (!success) { + error = GetLastError(); + lyxerr << "LyXComm: " + << errormsg(error) << endl; + if (!resetPipe(i, true)) + return false; + continue; + } + pipe_[i].state = is_outpipe ? WRITING_STATE + : READING_STATE; + break; + + case READING_STATE: + // Pending read operation + LASSERT(!is_outpipe, /**/); + if (!success || status == 0) { + if (!resetPipe(i, !success)) + return false; + continue; + } + pipe_[i].nbytes = status; + pipe_[i].state = WRITING_STATE; + break; + + case WRITING_STATE: + // Pending write operation + LASSERT(is_outpipe, /**/); + // Let's see whether we have a reply + if (!outbuf_.empty()) { + // Yep. Deliver it to all pipe + // instances if we get ownership + // of the mutex, otherwise we'll + // try again the next round. + DWORD result = WaitForSingleObject( + outbuf_mutex_, 200); + if (result == WAIT_OBJECT_0) { + DWORD j = MAX_CLIENTS; + while (j < MAX_PIPES) { + pipe_[j].iobuf = outbuf_; + ++j; + } + outbuf_.erase(); + } + ReleaseMutex(outbuf_mutex_); + } + if (pipe_[i].iobuf.empty()) + pipe_[i].pending_io = false; + break; + } + } + + // Operate according to the pipe state + switch (pipe_[i].state) { + case READING_STATE: + // The pipe instance is connected to a client + // and is ready to read a request. + LASSERT(!is_outpipe, /**/); + success = ReadFile(pipe_[i].handle, + pipe_[i].readbuf, PIPE_BUFSIZE - 1, + &pipe_[i].nbytes, &pipe_[i].overlap); + + if (success && pipe_[i].nbytes != 0) { + // The read operation completed successfully. + pipe_[i].pending_io = false; + pipe_[i].state = WRITING_STATE; + continue; + } + + error = GetLastError(); + + if (!success && error == ERROR_IO_PENDING) { + // The read operation is still pending. + pipe_[i].pending_io = true; + continue; + } + + success = error == ERROR_BROKEN_PIPE; + + // Client closed connection (ERROR_BROKEN_PIPE) or + // an error occurred; in either case, reset the pipe. + if (!success) { + lyxerr << "LyXComm: " << errormsg(error) << endl; + if (!pipe_[i].iobuf.empty()) { + lyxerr << "LyXComm: truncated command: " + << pipe_[i].iobuf << endl; + pipe_[i].iobuf.erase(); + } + } + if (!resetPipe(i, !success)) + return false; + break; + + case WRITING_STATE: + if (!is_outpipe) { + // The request was successfully read + // from the client; commit it. + ReadReadyEvent * event = new ReadReadyEvent(i); + QCoreApplication::postEvent(this, + static_cast<QEvent *>(event)); + // Wait for completion + while (pipe_[i].nbytes && !checkStopServer(100)) + ; + pipe_[i].pending_io = false; + pipe_[i].state = READING_STATE; + continue; + } + + // This is an output pipe instance. Initiate the + // overlapped write operation or monitor its progress. + + if (pipe_[i].pending_io) { + success = WriteFile(pipe_[i].handle, + pipe_[i].iobuf.c_str(), + pipe_[i].iobuf.length(), + &status, + &pipe_[i].overlap); + } + + if (success && !pipe_[i].iobuf.empty() + && status == pipe_[i].iobuf.length()) { + // The write operation completed successfully. + pipe_[i].iobuf.erase(); + pipe_[i].pending_io = false; + if (!resetPipe(i)) + return false; + continue; + } + + error = GetLastError(); + + if (success && error == ERROR_IO_PENDING) { + // 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. + checkStopServer(100); + pipe_[i].pending_io = true; + continue; + } + + success = error == ERROR_NO_DATA; + + // Client closed connection (ERROR_NO_DATA) or + // an error occurred; in either case, reset the pipe. + if (!success) { + lyxerr << "LyXComm: Error sending message: " + << pipe_[i].iobuf << "\nLyXComm: " + << errormsg(error) << endl; + } + if (!resetPipe(i, !success)) + return false; + break; + } + } + + ready_ = false; + closeHandles(); + return true; +} + + +void LyXComm::closeHandles() +{ + for (int i = 0; i < MAX_PIPES; ++i) { + if (event_[i]) { + ResetEvent(event_[i]); + CloseHandle(event_[i]); + event_[i] = 0; + } + if (pipe_[i].handle != INVALID_HANDLE_VALUE) { + CloseHandle(pipe_[i].handle); + pipe_[i].handle = INVALID_HANDLE_VALUE; + } + } +} + + +bool LyXComm::event(QEvent * e) +{ + if (e->type() == QEvent::User) { + read_ready(static_cast<ReadReadyEvent *>(e)->inpipe()); + return true; + } + return false; +} + + +bool LyXComm::checkStopServer(DWORD timeout) +{ + return WaitForSingleObject(stopserver_, timeout) == WAIT_OBJECT_0; +} + + +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)) { + DWORD const error = GetLastError(); + lyxerr << "LyXComm: Could not connect pipe " + << external_path(pipeName(index)) + << "\nLyXComm: " << errormsg(error) << endl; + return false; + } + + switch (GetLastError()) { + case ERROR_IO_PENDING: + // The overlapped connection is in progress. + pipe_[index].pending_io = true; + break; + + case ERROR_PIPE_CONNECTED: + // Client is already connected, so signal an event. + if (SetEvent(pipe_[index].overlap.hEvent)) + break; + // fall through + default: + // Anything else is an error. + DWORD const error = GetLastError(); + lyxerr << "LyXComm: An error occurred while connecting pipe " + << external_path(pipeName(index)) + << "\nLyXComm: " << errormsg(error) << endl; + return false; + } + + return true; +} + + +bool LyXComm::resetPipe(DWORD index, bool close_handle) +{ + // This method is called when an error occurs or when a client + // closes the connection. We first disconnect the pipe instance, + // then reconnect it, ready to wait for another client. + + if (!DisconnectNamedPipe(pipe_[index].handle)) { + DWORD const error = GetLastError(); + lyxerr << "LyXComm: Could not disconnect pipe " + << external_path(pipeName(index)) + << "\nLyXComm: " << errormsg(error) << endl; + // What to do now? Let's try whether re-creating the pipe helps. + close_handle = true; + } + + bool const is_outpipe = index >= MAX_CLIENTS; + + if (close_handle) { + DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND + : PIPE_ACCESS_INBOUND; + string const name = external_path(pipeName(index)); + + CloseHandle(pipe_[index].handle); + + pipe_[index].iobuf.erase(); + pipe_[index].handle = CreateNamedPipe(name.c_str(), + open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT, + MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE, + PIPE_TIMEOUT, NULL); + + if (pipe_[index].handle == INVALID_HANDLE_VALUE) { + DWORD const error = GetLastError(); + lyxerr << "LyXComm: Could not reset pipe " << name + << "\nLyXComm: " << errormsg(error) << endl; + return false; + } + } + + if (!startPipe(index)) + return false; + pipe_[index].state = pipe_[index].pending_io ? + CONNECTING_STATE : (is_outpipe ? WRITING_STATE + : READING_STATE); + return true; +} + + +void LyXComm::openConnection() +{ + LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection"); + + // If we are up, that's an error + if (ready_) { + LYXERR(Debug::LYXSERVER, "LyXComm: Already connected"); + return; + } + + if (pipename_.empty()) { + LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do"); + return; + } + + // Check whether the pipe name is being used by some other program. + if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) { + lyxerr << "LyXComm: Pipe " << external_path(inPipeName()) + << " already exists.\nMaybe another instance of LyX" + " is using it." << endl; + pipename_.erase(); + return; + } + + // Mutex with no initial owner for synchronized access to outbuf_ + outbuf_mutex_ = CreateMutex(NULL, FALSE, NULL); + if (!outbuf_mutex_) { + DWORD const error = GetLastError(); + lyxerr << "LyXComm: Could not create output buffer mutex" + << "\nLyXComm: " << errormsg(error) << endl; + pipename_.erase(); + return; + } + + // Manual-reset event, initial state = not signaled + stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!stopserver_) { + DWORD const error = GetLastError(); + lyxerr << "LyXComm: Could not create stop server event" + << "\nLyXComm: " << errormsg(error) << endl; + pipename_.erase(); + CloseHandle(outbuf_mutex_); + return; + } + + server_thread_ = CreateThread(NULL, 0, pipeServerWrapper, + static_cast<void *>(this), 0, NULL); + if (!server_thread_) { + DWORD const error = GetLastError(); + lyxerr << "LyXComm: Could not create pipe server thread" + << "\nLyXComm: " << errormsg(error) << endl; + pipename_.erase(); + CloseHandle(stopserver_); + CloseHandle(outbuf_mutex_); + return; + } +} + + +/// Close pipes +void LyXComm::closeConnection() +{ + LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection"); + + if (pipename_.empty()) { + LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do"); + return; + } + + if (!ready_) { + LYXERR(Debug::LYXSERVER, "LyXComm: Already disconnected"); + return; + } + + SetEvent(stopserver_); + // Wait for the pipe server to finish + WaitForSingleObject(server_thread_, INFINITE); + CloseHandle(server_thread_); + ResetEvent(stopserver_); + CloseHandle(stopserver_); + CloseHandle(outbuf_mutex_); +} + + +void LyXComm::emergencyCleanup() +{ + if (ready_) { + SetEvent(stopserver_); + // Forcibly terminate the pipe server thread if it does + // not finish quickly. + if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0) { + TerminateThread(server_thread_, 0); + ready_ = false; + closeHandles(); + } + CloseHandle(server_thread_); + ResetEvent(stopserver_); + CloseHandle(stopserver_); + CloseHandle(outbuf_mutex_); + } +} + + +void LyXComm::read_ready(DWORD inpipe) +{ + // Turn the pipe buffer into a C string + DWORD const nbytes = pipe_[inpipe].nbytes; + pipe_[inpipe].readbuf[nbytes] = '\0'; + + pipe_[inpipe].iobuf += rtrim(pipe_[inpipe].readbuf, "\r"); + + // Commit any commands read + while (pipe_[inpipe].iobuf.find('\n') != string::npos) { + // split() grabs the entire string if + // the delim /wasn't/ found. ?:-P + string cmd; + pipe_[inpipe].iobuf = split(pipe_[inpipe].iobuf, cmd, '\n'); + cmd = rtrim(cmd, "\r"); + LYXERR(Debug::LYXSERVER, "LyXComm: nbytes:" << nbytes + << ", iobuf:" << pipe_[inpipe].iobuf + << ", cmd:" << cmd); + if (!cmd.empty()) + clientcb_(client_, cmd); + //\n or not \n? + } + // Signal that we are done. + pipe_[inpipe].nbytes = 0; +} + + +void LyXComm::send(string const & msg) +{ + if (msg.empty()) { + lyxerr << "LyXComm: Request to send empty string. Ignoring." + << endl; + return; + } + + LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\''); + + if (pipename_.empty()) + return; + + if (!ready_) { + lyxerr << "LyXComm: Pipes are closed. Could not send " + << msg << endl; + return; + } + + // Request ownership of the outbuf_mutex_ + DWORD result = WaitForSingleObject(outbuf_mutex_, PIPE_TIMEOUT); + + if (result == WAIT_OBJECT_0) { + // If a client doesn't care to read a reply (JabRef is one + // such client), the output buffer could grow without limit. + // So, we empty it when its size is larger than PIPE_BUFSIZE. + if (outbuf_.size() > PIPE_BUFSIZE) + outbuf_.erase(); + outbuf_ += msg; + ReleaseMutex(outbuf_mutex_); + } else { + // Something is fishy, better resetting the connection. + DWORD const error = GetLastError(); + lyxerr << "LyXComm: Error sending message: " << msg + << "\nLyXComm: " << errormsg(error) + << "\nLyXComm: Resetting connection" << endl; + ReleaseMutex(outbuf_mutex_); + closeConnection(); + openConnection(); + } +} + + +string const LyXComm::pipeName(DWORD index) const +{ + return index < MAX_CLIENTS ? inPipeName() : outPipeName(); +} + + +#elif !defined (HAVE_MKFIFO) // We provide a stub class that disables the lyxserver. LyXComm::LyXComm(string const &, Server *, ClientCallbackfct) @@ -537,3 +1145,7 @@ void Server::notifyClient(string const & } // namespace lyx + +#ifdef _WIN32 +#include "Server_moc.cpp" +#endif Index: src/Server.h =================================================================== --- src/Server.h (revision 31369) +++ src/Server.h (working copy) @@ -6,6 +6,7 @@ * * \author Lars Gullik Bjønnes * \author Jean-Marc Lasgouttes + * \author Enrico Forestieri * * Full author contact details are available in file CREDITS. */ @@ -15,6 +16,12 @@ #include <boost/signals/trackable.hpp> +#ifdef _WIN32 +#include <windows.h> +#include <QObject> +#include <QEvent> +#endif + namespace lyx { @@ -29,7 +36,46 @@ class Server; This class encapsulates all the dirty communication and thus provides a clean string interface. */ +#ifndef _WIN32 class LyXComm : public boost::signals::trackable { +#else +class LyXComm : public QObject { + Q_OBJECT + + friend DWORD WINAPI pipeServerWrapper(void *); + +public: + /// Max number of clients + enum { MAX_CLIENTS = 10 }; + +private: + /// Max number of pipe instances + enum { MAX_PIPES = 2 * MAX_CLIENTS }; + + /// I/O buffer size + enum { PIPE_BUFSIZE = 512 }; + + /// Pipe client time-out + enum { PIPE_TIMEOUT = 5000 }; + + /// Pipe states + enum PipeState { + CONNECTING_STATE, + READING_STATE, + WRITING_STATE + }; + + /// Pipe instances + typedef struct { + OVERLAPPED overlap; + HANDLE handle; + std::string iobuf; + char readbuf[PIPE_BUFSIZE]; + DWORD nbytes; + PipeState state; + bool pending_io; + } PipeInst; +#endif public: /** When we receive a message, we send it to a client. This is one of the small things that would have been a lot @@ -50,7 +96,11 @@ public: void send(std::string const &); /// asynch ready-to-be-read notification +#ifndef _WIN32 void read_ready(); +#else + void read_ready(DWORD); +#endif private: /// the filename of the in pipe @@ -65,6 +115,7 @@ private: /// Close pipes void closeConnection(); +#ifndef _WIN32 /// start a pipe int startPipe(std::string const &, bool); @@ -76,6 +127,46 @@ private: /// This is -1 if not open int outfd_; +#else + /// The pipe server returns false when exiting due to an error + bool pipeServer(); + + /// Start an overlapped connection + bool startPipe(DWORD); + + /// Reset an overlapped connection + bool resetPipe(DWORD, bool close_handle = false); + + /// Close event and pipe handles + void closeHandles(); + + /// Catch pipe ready-to-be-read notification + bool event(QEvent *); + + /// Check whether the pipe server must be stopped + bool checkStopServer(DWORD timeout = 0); + + /// The filename of a (in or out) pipe instance + std::string const pipeName(DWORD) const; + + /// Pipe instances + PipeInst pipe_[MAX_PIPES]; + + /// Pipe server control events + HANDLE event_[MAX_PIPES + 1]; + + /// Reply buffer + std::string outbuf_; + + /// Synchronize access to outbuf_ + HANDLE outbuf_mutex_; + + /// Windows event for stopping the pipe server + HANDLE stopserver_; + + /// Pipe server thread handle + HANDLE server_thread_; +#endif /// Are we up and running? bool ready_; @@ -118,7 +209,11 @@ public: private: /// Names and number of current clients +#ifndef _WIN32 enum { MAX_CLIENTS = 10 }; +#else + enum { MAX_CLIENTS = LyXComm::MAX_CLIENTS }; +#endif /// std::string clients_[MAX_CLIENTS]; /// Index: development/cmake/src/CMakeLists.txt =================================================================== --- development/cmake/src/CMakeLists.txt (revision 31369) +++ development/cmake/src/CMakeLists.txt (working copy) @@ -34,8 +34,10 @@ if (ASPELL_FOUND) set(lyx_sources ${lyx_sources} ${TOP_SRC_DIR}/src/ASpell.cpp) endif() +lyx_automoc(${TOP_SRC_DIR}/src/Server.cpp) + include_directories(${CMAKE_CURRENT_BINARY_DIR} - ${ZLIB_INCLUDE_DIR}) + ${ZLIB_INCLUDE_DIR} ${QT_INCLUDES}) lyx_add_msvc_pch(lyx) Index: development/cmake/modules/LyXMacros.cmake =================================================================== --- development/cmake/modules/LyXMacros.cmake (revision 31369) +++ development/cmake/modules/LyXMacros.cmake (working copy) @@ -105,9 +105,12 @@ macro(LYX_AUTOMOC) set(_moc ${CMAKE_CURRENT_BINARY_DIR}/${_current_MOC}) #set(_moc ${_abs_PATH}/${_current_MOC}) + if (WIN32) + set(_def -D_WIN32) + endif() add_custom_command(OUTPUT ${_moc} COMMAND ${QT_MOC_EXECUTABLE} - ARGS ${_moc_INCS} ${_header} -o ${_moc} + ARGS ${_def} ${_moc_INCS} ${_header} -o ${_moc} MAIN_DEPENDENCY ${_header}) macro_add_file_dependencies(${_abs_FILE} ${_moc}) endforeach (_current_MOC_INC) Index: development/scons/SConstruct =================================================================== --- development/scons/SConstruct (revision 31369) +++ development/scons/SConstruct (working copy) @@ -903,6 +903,7 @@ result = utils.createConfigFile(conf, ('locale.h', 'HAVE_LOCALE_H', 'c'), ('process.h', 'HAVE_PROCESS_H', 'c'), ('stdlib.h', 'HAVE_STDLIB_H', 'c'), + ('string.h', 'HAVE_STRING_H', 'c'), ('sys/stat.h', 'HAVE_SYS_STAT_H', 'c'), ('sys/time.h', 'HAVE_SYS_TIME_H', 'c'), ('sys/types.h', 'HAVE_SYS_TYPES_H', 'c'), @@ -1287,30 +1288,29 @@ if platform_name == 'cygwin': # to be built with all the include directories etc # if frontend == 'qt4': - frontend_env = env.Clone() - frontend_env['BUILDERS']['qtResource'] = Builder(action = utils.env_qtResource) + env['BUILDERS']['qtResource'] = Builder(action = utils.env_qtResource) # handle qt related user specified paths # set environment so that moc etc can be found even if its path is not set properly - if frontend_env.has_key('qt_dir') and frontend_env['qt_dir']: - frontend_env['QTDIR'] = frontend_env['qt_dir'] - if os.path.isdir(os.path.join(frontend_env['qt_dir'], 'bin')): - os.environ['PATH'] += os.pathsep + os.path.join(frontend_env['qt_dir'], 'bin') - frontend_env.PrependENVPath('PATH', os.path.join(frontend_env['qt_dir'], 'bin')) - if os.path.isdir(os.path.join(frontend_env['qt_dir'], 'lib')): - frontend_env.PrependENVPath('PKG_CONFIG_PATH', os.path.join(frontend_env['qt_dir'], 'lib')) + if env.has_key('qt_dir') and env['qt_dir']: + env['QTDIR'] = env['qt_dir'] + if os.path.isdir(os.path.join(env['qt_dir'], 'bin')): + os.environ['PATH'] += os.pathsep + os.path.join(env['qt_dir'], 'bin') + env.PrependENVPath('PATH', os.path.join(env['qt_dir'], 'bin')) + if os.path.isdir(os.path.join(env['qt_dir'], 'lib')): + env.PrependENVPath('PKG_CONFIG_PATH', os.path.join(env['qt_dir'], 'lib')) # if separate qt_lib_path is given - if frontend_env.has_key('qt_lib_path') and frontend_env['qt_lib_path']: - qt_lib_path = frontend_env.subst('$qt_lib_path') - frontend_env.AppendUnique(LIBPATH = [qt_lib_path]) - frontend_env.PrependENVPath('PKG_CONFIG_PATH', qt_lib_path) + if env.has_key('qt_lib_path') and env['qt_lib_path']: + qt_lib_path = env.subst('$qt_lib_path') + env.AppendUnique(LIBPATH = [qt_lib_path]) + env.PrependENVPath('PKG_CONFIG_PATH', qt_lib_path) else: qt_lib_path = None # if separate qt_inc_path is given - if frontend_env.has_key('qt_inc_path') and frontend_env['qt_inc_path']: - qt_inc_path = frontend_env['qt_inc_path'] + if env.has_key('qt_inc_path') and env['qt_inc_path']: + qt_inc_path = env['qt_inc_path'] else: qt_inc_path = None @@ -1320,18 +1320,20 @@ if frontend == 'qt4': # NOTE: I have to patch qt4.py since it does not automatically # process .C file!!! (add to cxx_suffixes ) # - frontend_env.Tool('qt4', [scons_dir]) - frontend_env['QT_AUTOSCAN'] = 0 - frontend_env['QT4_AUTOSCAN'] = 0 - frontend_env['QT4_UICDECLFLAGS'] = '-tr lyx::qt_' + env.Tool('qt4', [scons_dir]) + env['QT_AUTOSCAN'] = 0 + env['QT4_AUTOSCAN'] = 0 + env['QT4_UICDECLFLAGS'] = '-tr lyx::qt_' + if platform_name == 'win32': + env['QT4_MOCFROMHFLAGS'] = '-D_WIN32' if qt_lib_path is None: - qt_lib_path = os.path.join(frontend_env.subst('$QTDIR'), 'lib') + qt_lib_path = os.path.join(env.subst('$QTDIR'), 'lib') if qt_inc_path is None: - qt_inc_path = os.path.join(frontend_env.subst('$QTDIR'), 'include') + qt_inc_path = os.path.join(env.subst('$QTDIR'), 'include') - conf = Configure(frontend_env, + conf = Configure(env, custom_tests = { 'CheckPackage' : utils.checkPackage, 'CheckCommand' : utils.checkCommand, @@ -1340,10 +1342,10 @@ if frontend == 'qt4': succ = False # first: try pkg_config - if frontend_env['HAS_PKG_CONFIG']: + if env['HAS_PKG_CONFIG']: succ = conf.CheckPackage('QtCore') or conf.CheckPackage('QtCore4') # FIXME: use pkg_config information? - #frontend_env['QT4_PKG_CONFIG'] = succ + #env['QT4_PKG_CONFIG'] = succ # second: try to link to it if not succ: # Under linux, I can test the following perfectly @@ -1375,7 +1377,7 @@ if frontend == 'qt4': else: qt_lib_suffix = '' use_qt_debug_libs = False - frontend_env.EnableQt4Modules(qt_libs, debug = (mode == 'debug' and use_qt_debug_libs)) + env.EnableQt4Modules(qt_libs, debug = (mode == 'debug' and use_qt_debug_libs)) frontend_libs = [x + qt_lib_suffix for x in qt_libs] qtcore_lib = ['QtCore' + qt_lib_suffix] @@ -1388,11 +1390,11 @@ if frontend == 'qt4': # NOTE: previously, lyx.exe had to be linked to some qt manifest to work. # For some unknown changes in msvc or qt, this is no longer needed. if use_vc: - frontend_env['LINKCOM'] = [frontend_env['LINKCOM'], \ + env['LINKCOM'] = [env['LINKCOM'], \ 'mt.exe /MANIFEST %s /outputresource:$TARGET;1' % \ env.File('$BUILDDIR/lyx.exe.manifest').path] - frontend_env = conf.Finish() + env = conf.Finish() # # Report results @@ -1435,7 +1437,7 @@ env.SConsignFile(os.path.join(Dir(env['B env.BuildDir('$BUILDDIR/boost', '$TOP_SRCDIR/boost/libs', duplicate = 0) env.BuildDir('$BUILDDIR/intl', '$TOP_SRCDIR/intl', duplicate = 0) env.BuildDir('$BUILDDIR/src', '$TOP_SRCDIR/src', duplicate = 0) -frontend_env.BuildDir('$BUILDDIR/src', '$TOP_SRCDIR/src', duplicate = 0) +env.BuildDir('$BUILDDIR/src', '$TOP_SRCDIR/src', duplicate = 0) print "Building all targets recursively" @@ -1493,12 +1495,12 @@ if (included_gettext and not libExists(' # # src/support # -frontend_env['QT4_MOCHPREFIX'] = '' -frontend_env['QT4_MOCHSUFFIX'] = '_moc.cpp' +env['QT4_MOCHPREFIX'] = '' +env['QT4_MOCHSUFFIX'] = '_moc.cpp' -support_moced_files = [frontend_env.Moc4('$BUILDDIR/src/support/%s' % x) +support_moced_files = [env.Moc4('$BUILDDIR/src/support/%s' % x) for x in src_support_header_files ] -support = frontend_env.StaticLibrary( +support = env.StaticLibrary( target = '$LOCALLIBPATH/support', source = ['$BUILDDIR/src/support/%s' % x for x in src_support_files], CCFLAGS = [ @@ -1511,97 +1513,18 @@ support = frontend_env.StaticLibrary( ) Alias('support', support) - -# -# src/mathed -# -mathed = env.StaticLibrary( - target = '$LOCALLIBPATH/mathed', - source = ['$BUILDDIR/src/mathed/%s' % x for x in src_mathed_files] -) -Alias('mathed', mathed) - - -# -# src/insets -# -insets = env.StaticLibrary( - target = '$LOCALLIBPATH/insets', - source = ['$BUILDDIR/src/insets/%s' % x for x in src_insets_files] -) -Alias('insets', insets) - - -# -# src/frontends -# -frontends = env.StaticLibrary( - target = '$LOCALLIBPATH/frontends', - source = ['$BUILDDIR/src/frontends/%s' % x for x in src_frontends_files] -) -Alias('frontends', frontends) - - -# -# src/graphics -# -graphics = env.StaticLibrary( - target = '$LOCALLIBPATH/graphics', - source = ['$BUILDDIR/src/graphics/%s' % x for x in src_graphics_files] -) -Alias('graphics', graphics) - - -# -# src/frontend/qt4 -# -# tells scons how to get these moced files, although not all moced files are needed -# (or are actually generated). -qt4_moced_files = [frontend_env.Moc4('$BUILDDIR/src/frontends/qt4/%s' % x) - for x in src_frontends_qt4_header_files ] -ui_files = [frontend_env.Uic4('$BUILDDIR/src/frontends/qt4/ui/%s' % x.split('.')[0]) - for x in src_frontends_qt4_ui_files] -resource = frontend_env.Qrc(frontend_env.qtResource( - '$BUILDDIR/src/frontends/qt4/Resource.qrc', - ['$TOP_SRCDIR/lib/images/%s' % x for x in lib_images_files] + - ['$TOP_SRCDIR/lib/images/math/%s' % x for x in lib_images_math_files] + - ['$TOP_SRCDIR/lib/images/commands/%s' % x for x in lib_images_commands_files])) -# -# moc qt4_moc_files, the moced files are included in the original files -# -qt4 = frontend_env.StaticLibrary( - target = '$LOCALLIBPATH/qt4', - source = ['$BUILDDIR/src/frontends/qt4/%s' % x for x in src_frontends_qt4_files] + resource, - CPPPATH = [ - '$CPPPATH', - '$BUILDDIR/src', - '$BUILDDIR/src/images', - '$BUILDDIR/src/frontends', - '$BUILDDIR/src/frontends/qt4', - '$BUILDDIR/src/frontends/qt4/ui', - ], - CCFLAGS = [ - '$CCFLAGS', - '-DHAVE_CONFIG_H', - '-DQT_NO_STL', - '-DQT_NO_KEYWORDS', - ] -) -Alias('qt4', qt4) - - # -# src/client +# lyxclient # if env['HAVE_FCNTL']: - client = frontend_env.Program( + client = env.Program( target = '$BUILDDIR/src/client/lyxclient', LIBS = ['support'] + intl_libs + system_libs + socket_libs + boost_libraries + qtcore_lib, source = ['$BUILDDIR/src/client/%s' % x for x in src_client_files] + \ - utils.createResFromIcon(frontend_env, 'lyx.ico', '$LOCALLIBPATH/client.rc') + utils.createResFromIcon(env, 'lyx.ico', '$LOCALLIBPATH/client.rc') ) - Alias('client', frontend_env.Command(os.path.join('$BUILDDIR', os.path.split(str(client[0]))[1]), + Alias('client', env.Command(os.path.join('$BUILDDIR', os.path.split(str(client[0]))[1]), client, [Copy('$TARGET', '$SOURCE')])) else: client = None @@ -1612,25 +1535,25 @@ Alias('client', client) # tex2lyx # for file in src_tex2lyx_copied_files + src_tex2lyx_copied_header_files: - frontend_env.Command('$BUILDDIR/src/tex2lyx/'+file, '$TOP_SRCDIR/src/'+file, + env.Command('$BUILDDIR/src/tex2lyx/'+file, '$TOP_SRCDIR/src/'+file, [Copy('$TARGET', '$SOURCE')]) -tex2lyx = frontend_env.Program( +tex2lyx = env.Program( target = '$BUILDDIR/src/tex2lyx/tex2lyx', LIBS = ['support'] + boost_libraries + intl_libs + system_libs + qtcore_lib, source = ['$BUILDDIR/src/tex2lyx/%s' % x for x in src_tex2lyx_files + src_tex2lyx_copied_files] + \ - utils.createResFromIcon(frontend_env, 'lyx.ico', '$LOCALLIBPATH/tex2lyx.rc'), + utils.createResFromIcon(env, 'lyx.ico', '$LOCALLIBPATH/tex2lyx.rc'), CPPPATH = ['$BUILDDIR/src/tex2lyx', '$BUILDDIR/src', '$CPPPATH'], LIBPATH = ['#$LOCALLIBPATH', '$LIBPATH'], CCFLAGS = ['$CCFLAGS', '-DTEX2LYX'], ) -Alias('tex2lyx', frontend_env.Command(os.path.join('$BUILDDIR', os.path.split(str(tex2lyx[0]))[1]), +Alias('tex2lyx', env.Command(os.path.join('$BUILDDIR', os.path.split(str(tex2lyx[0]))[1]), tex2lyx, [Copy('$TARGET', '$SOURCE')])) Alias('tex2lyx', tex2lyx) # -# src/ +# Build lyx with given frontend # if env.has_key('USE_ASPELL') and env['USE_ASPELL']: src_post_files.append('ASpell.cpp') @@ -1639,37 +1562,50 @@ elif env.has_key('USE_PSPELL') and env[' elif env.has_key('USE_ISPELL') and env['USE_ISPELL']: src_post_files.append('ISpell.cpp') -# msvc requires at least one source file with main() -# so I exclude main.cpp from lyxbase -lyxbase_pre = env.StaticLibrary( - target = '$LOCALLIBPATH/lyxbase_pre', - source = ['$BUILDDIR/src/%s' % x for x in src_pre_files] -) -lyxbase_post = env.StaticLibrary( - target = '$LOCALLIBPATH/lyxbase_post', - source = ["$BUILDDIR/src/%s" % x for x in src_post_files] -) -Alias('lyxbase', lyxbase_pre) -Alias('lyxbase', lyxbase_post) - +# tells scons how to get these moced files, although not all moced files are needed +# (or are actually generated). +qt4_moced_files = [env.Moc4('$BUILDDIR/src/frontends/qt4/%s' % x) + for x in src_frontends_qt4_header_files ] +src_moced_files = [env.Moc4('$BUILDDIR/src/%s' % x) + for x in src_header_files ] +ui_files = [env.Uic4('$BUILDDIR/src/frontends/qt4/ui/%s' % x.split('.')[0]) + for x in src_frontends_qt4_ui_files] +resource = env.Qrc(env.qtResource( + '$BUILDDIR/src/frontends/qt4/Resource.qrc', + ['$TOP_SRCDIR/lib/images/%s' % x for x in lib_images_files] + + ['$TOP_SRCDIR/lib/images/math/%s' % x for x in lib_images_math_files] + + ['$TOP_SRCDIR/lib/images/commands/%s' % x for x in lib_images_commands_files])) -# -# Build lyx with given frontend -# -lyx = frontend_env.Program( +lyx = env.Program( target = '$BUILDDIR/lyx', - source = ['$BUILDDIR/src/main.cpp'] + \ - utils.createResFromIcon(frontend_env, 'lyx.ico', '$LOCALLIBPATH/lyx.rc'), + source = ['$BUILDDIR/src/main.cpp'] + + ['$BUILDDIR/src/frontends/qt4/%s' % x for x in src_frontends_qt4_files] + + resource + + ['$BUILDDIR/src/graphics/%s' % x for x in src_graphics_files] + + ['$BUILDDIR/src/mathed/%s' % x for x in src_mathed_files] + + ['$BUILDDIR/src/insets/%s' % x for x in src_insets_files] + + ['$BUILDDIR/src/frontends/%s' % x for x in src_frontends_files] + + ['$BUILDDIR/src/%s' % x for x in src_pre_files] + + ["$BUILDDIR/src/%s" % x for x in src_post_files] + + utils.createResFromIcon(env, 'lyx.ico', '$LOCALLIBPATH/lyx.rc'), + CPPPATH = [ + '$CPPPATH', + '$BUILDDIR/src', + '$BUILDDIR/src/images', + '$BUILDDIR/src/frontends', + '$BUILDDIR/src/frontends/qt4', + '$BUILDDIR/src/frontends/qt4/ui', + ], + CCFLAGS = [ + '$CCFLAGS', + '-DHAVE_CONFIG_H', + '-DQT_NO_STL', + '-DQT_NO_KEYWORDS', + ], LIBS = [ - 'lyxbase_pre', - 'mathed', - 'insets', - 'frontends', - frontend, - 'graphics', 'support', ] + - boost_libraries + ['lyxbase_post'] + + boost_libraries + frontend_libs + intl_libs + socket_libs + Index: config/lyxinclude.m4 =================================================================== --- config/lyxinclude.m4 (revision 31369) +++ config/lyxinclude.m4 (working copy) @@ -610,6 +610,7 @@ AC_ARG_WITH(packaging, AC_MSG_RESULT($lyx_use_packaging) lyx_install_macosx=false lyx_install_cygwin=false +lyx_install_windows=false case $lyx_use_packaging in macosx) AC_DEFINE(USE_MACOSX_PACKAGING, 1, [Define to 1 if LyX should use a MacOS X application bundle file layout]) PACKAGE=LyX${version_suffix} @@ -627,7 +628,8 @@ case $lyx_use_packaging in libdir='${prefix}/Resources' datarootdir='${prefix}/Resources' pkgdatadir='${datadir}' - mandir='${prefix}/Resources/man' ;; + mandir='${prefix}/Resources/man' + lyx_install_windows=true ;; posix) AC_DEFINE(USE_POSIX_PACKAGING, 1, [Define to 1 if LyX should use a POSIX-style file layout]) PACKAGE=lyx${version_suffix} program_suffix=$version_suffix @@ -640,6 +642,7 @@ case $lyx_use_packaging in esac AM_CONDITIONAL(INSTALL_MACOSX, $lyx_install_macosx) AM_CONDITIONAL(INSTALL_CYGWIN, $lyx_install_cygwin) +AM_CONDITIONAL(INSTALL_WINDOWS, $lyx_install_windows) dnl Next two lines are only for autoconf <= 2.59 datadir='${datarootdir}' AC_SUBST(datarootdir)