mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-22 01:59:02 +00:00
Implement the LyXServer on Windows.
Only for autotools, I don't know how to update cmake and scons, sorry. For cmake and scons, you should make sure that moc is called on Server.h using the -D_WIN32 option. In order to enable the server, specify the LyXServer pipe in Tools->Preferences->Paths. The path to be entered there must have the form "\\.\pipe\nameofyourchoice" (without quotes). After that, you can send commands to LyX. For example, if the pipe path is \\.\pipe\lyxpipe, typing the following in a terminal: echo LYXCMD:test:file-open > \\.\pipe\lyxpipe.in type \\.\pipe\lyxpipe.out brings up the file dialog and returns the acknowledgment from LyX. Beware of spaces when using cmd.exe. For example, the following: echo LYXCMD:test:file-open:foo.lyx> \\.\pipe\lyxpipe.in will correctly load the document named foo.lyx, but echo LYXCMD:test:file-open:foo.lyx > \\.\pipe\lyxpipe.in (notice the space before the redirection) will try to load a document whose name is "foo.lyx .lyx" because cmd.exe will also pass the space (sigh). git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@31189 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
818ae93e0c
commit
fc97861803
@ -611,6 +611,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}
|
||||
@ -628,7 +629,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
|
||||
@ -641,6 +643,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)
|
||||
|
@ -297,6 +297,22 @@ liblyxcore_a_SOURCES = $(SOURCEFILESCORE) $(STANDALONEFILES) $(HEADERFILESCORE)
|
||||
|
||||
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_a_DEPENDENCIES = $(MOCEDFILES)
|
||||
|
||||
endif
|
||||
|
||||
############################### Graphics ##############################
|
||||
|
||||
noinst_LIBRARIES += liblyxgraphics.a
|
||||
|
297
src/Server.cpp
297
src/Server.cpp
@ -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.
|
||||
*/
|
||||
@ -49,9 +50,14 @@
|
||||
#include "support/debug.h"
|
||||
#include "support/FileName.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 +66,7 @@
|
||||
|
||||
using namespace std;
|
||||
using namespace lyx::support;
|
||||
using os::external_path;
|
||||
|
||||
namespace lyx {
|
||||
|
||||
@ -69,7 +76,291 @@ namespace lyx {
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if !defined (HAVE_MKFIFO)
|
||||
#if defined(_WIN32)
|
||||
|
||||
class PipeEvent : public QEvent {
|
||||
public:
|
||||
///
|
||||
PipeEvent(HANDLE inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
|
||||
{}
|
||||
///
|
||||
HANDLE pipe() const { return inpipe_; }
|
||||
|
||||
private:
|
||||
HANDLE inpipe_;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
char * errormsg()
|
||||
{
|
||||
void * msgbuf;
|
||||
DWORD error = GetLastError();
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &msgbuf, 0, NULL);
|
||||
return static_cast<char *>(msgbuf);
|
||||
}
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
DWORD WINAPI pipeServerWrapper(void * arg)
|
||||
{
|
||||
LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
|
||||
lyxcomm->pipeServer();
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace anon
|
||||
|
||||
LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
|
||||
: pipename_(pip), client_(cli), clientcb_(ccb)
|
||||
{
|
||||
ready_ = false;
|
||||
openConnection();
|
||||
}
|
||||
|
||||
|
||||
void LyXComm::pipeServer()
|
||||
{
|
||||
int const bufsize = 1024;
|
||||
HANDLE inpipe;
|
||||
|
||||
outpipe_ = CreateNamedPipe(external_path(outPipeName()).c_str(),
|
||||
PIPE_ACCESS_OUTBOUND, PIPE_NOWAIT,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
bufsize, 0, 0, NULL);
|
||||
if (outpipe_ == INVALID_HANDLE_VALUE) {
|
||||
lyxerr << "LyXComm: Could not create pipe "
|
||||
<< outPipeName() << '\n' << errormsg() << endl;
|
||||
return;
|
||||
}
|
||||
ConnectNamedPipe(outpipe_, NULL);
|
||||
// Now change to blocking mode
|
||||
DWORD mode = PIPE_WAIT;
|
||||
SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
|
||||
|
||||
while (!checkStopServerEvent()) {
|
||||
inpipe = CreateNamedPipe(external_path(inPipeName()).c_str(),
|
||||
PIPE_ACCESS_INBOUND, PIPE_WAIT,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
0, bufsize, 0, NULL);
|
||||
if (inpipe == INVALID_HANDLE_VALUE) {
|
||||
lyxerr << "LyXComm: Could not create pipe "
|
||||
<< inPipeName() << '\n' << errormsg() << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
BOOL connected = ConnectNamedPipe(inpipe, NULL);
|
||||
// Check whether we are signaled to shutdown the pipe server.
|
||||
if (checkStopServerEvent()) {
|
||||
CloseHandle(inpipe);
|
||||
break;
|
||||
}
|
||||
if (connected || GetLastError() == ERROR_PIPE_CONNECTED) {
|
||||
PipeEvent * event = new PipeEvent(inpipe);
|
||||
QCoreApplication::postEvent(this,
|
||||
static_cast<QEvent *>(event));
|
||||
} else
|
||||
CloseHandle(inpipe);
|
||||
}
|
||||
CloseHandle(outpipe_);
|
||||
}
|
||||
|
||||
|
||||
bool LyXComm::event(QEvent * e)
|
||||
{
|
||||
if (e->type() == QEvent::User) {
|
||||
read_ready(static_cast<PipeEvent *>(e)->pipe());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
BOOL LyXComm::checkStopServerEvent()
|
||||
{
|
||||
return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
|
||||
void LyXComm::openConnection()
|
||||
{
|
||||
LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
|
||||
|
||||
// If we are up, that's an error
|
||||
if (ready_) {
|
||||
lyxerr << "LyXComm: Already connected" << endl;
|
||||
return;
|
||||
}
|
||||
// We assume that we don't make it
|
||||
ready_ = false;
|
||||
|
||||
if (pipename_.empty()) {
|
||||
LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
|
||||
return;
|
||||
}
|
||||
|
||||
stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
DWORD tid;
|
||||
HANDLE thread = CreateThread(NULL, 0, pipeServerWrapper,
|
||||
static_cast<void *>(this), 0, &tid);
|
||||
if (!thread) {
|
||||
lyxerr << "LyXComm: Could not create pipe server thread: "
|
||||
<< errormsg() << endl;
|
||||
return;
|
||||
} else
|
||||
CloseHandle(thread);
|
||||
|
||||
// We made it!
|
||||
ready_ = true;
|
||||
LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
|
||||
}
|
||||
|
||||
|
||||
/// 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_) {
|
||||
LYXERR0("LyXComm: Already disconnected");
|
||||
return;
|
||||
}
|
||||
|
||||
SetEvent(stopserver_);
|
||||
HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
|
||||
GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hpipe != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(hpipe);
|
||||
|
||||
ready_ = false;
|
||||
}
|
||||
|
||||
|
||||
void LyXComm::emergencyCleanup()
|
||||
{
|
||||
if (!pipename_.empty()) {
|
||||
SetEvent(stopserver_);
|
||||
HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
|
||||
GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hpipe != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(hpipe);
|
||||
}
|
||||
}
|
||||
|
||||
void LyXComm::read_ready(HANDLE inpipe)
|
||||
{
|
||||
string read_buffer_;
|
||||
read_buffer_.erase();
|
||||
|
||||
int const charbuf_size = 100;
|
||||
char charbuf[charbuf_size];
|
||||
|
||||
DWORD status;
|
||||
while (ReadFile(inpipe, charbuf, charbuf_size - 1, &status, NULL)) {
|
||||
if (status > 0) {
|
||||
charbuf[status] = '\0'; // turn it into a c string
|
||||
read_buffer_ += rtrim(charbuf, "\r");
|
||||
// commit any commands read
|
||||
while (read_buffer_.find('\n') != string::npos) {
|
||||
// split() grabs the entire string if
|
||||
// the delim /wasn't/ found. ?:-P
|
||||
string cmd;
|
||||
read_buffer_= split(read_buffer_, cmd, '\n');
|
||||
cmd = rtrim(cmd, "\r");
|
||||
LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
|
||||
<< ", read_buffer_:" << read_buffer_
|
||||
<< ", cmd:" << cmd);
|
||||
if (!cmd.empty())
|
||||
clientcb_(client_, cmd);
|
||||
//\n or not \n?
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GetLastError() != ERROR_BROKEN_PIPE) {
|
||||
// An error occurred
|
||||
LYXERR0("LyXComm: " << errormsg());
|
||||
if (!read_buffer_.empty()) {
|
||||
LYXERR0("LyXComm: truncated command: " << read_buffer_);
|
||||
read_buffer_.erase();
|
||||
}
|
||||
}
|
||||
// Client closed the pipe, so disconnect and close.
|
||||
DisconnectNamedPipe(inpipe);
|
||||
CloseHandle(inpipe);
|
||||
FlushFileBuffers(outpipe_);
|
||||
DisconnectNamedPipe(outpipe_);
|
||||
// Temporarily change to non-blocking mode otherwise
|
||||
// ConnectNamedPipe() would block waiting for a connection.
|
||||
DWORD mode = PIPE_NOWAIT;
|
||||
SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
|
||||
ConnectNamedPipe(outpipe_, NULL);
|
||||
mode = PIPE_WAIT;
|
||||
SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
void LyXComm::send(string const & msg)
|
||||
{
|
||||
if (msg.empty()) {
|
||||
LYXERR0("LyXComm: Request to send empty string. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
|
||||
|
||||
if (pipename_.empty()) return;
|
||||
|
||||
if (!ready_) {
|
||||
LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
|
||||
return;
|
||||
}
|
||||
|
||||
bool success;
|
||||
int count = 0;
|
||||
do {
|
||||
DWORD sent;
|
||||
success = WriteFile(outpipe_, msg.c_str(), msg.length(), &sent, NULL);
|
||||
if (!success) {
|
||||
DWORD error = GetLastError();
|
||||
if (error == ERROR_NO_DATA) {
|
||||
DisconnectNamedPipe(outpipe_);
|
||||
DWORD mode = PIPE_NOWAIT;
|
||||
SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
|
||||
ConnectNamedPipe(outpipe_, NULL);
|
||||
mode = PIPE_WAIT;
|
||||
SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
|
||||
} else if (error != ERROR_PIPE_LISTENING)
|
||||
break;
|
||||
}
|
||||
Sleep(100);
|
||||
++count;
|
||||
} while (!success && count < 100);
|
||||
|
||||
if (!success) {
|
||||
lyxerr << "LyXComm: Error sending message: " << msg
|
||||
<< '\n' << errormsg()
|
||||
<< "\nLyXComm: Resetting connection" << endl;
|
||||
closeConnection();
|
||||
openConnection();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#elif !defined (HAVE_MKFIFO)
|
||||
// We provide a stub class that disables the lyxserver.
|
||||
|
||||
LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
|
||||
@ -537,3 +828,7 @@ void Server::notifyClient(string const & s)
|
||||
|
||||
|
||||
} // namespace lyx
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "moc_Server.cpp"
|
||||
#endif
|
||||
|
34
src/Server.h
34
src/Server.h
@ -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,12 @@ 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
|
||||
#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 +62,14 @@ public:
|
||||
void send(std::string const &);
|
||||
|
||||
/// asynch ready-to-be-read notification
|
||||
#ifndef _WIN32
|
||||
void read_ready();
|
||||
#else
|
||||
void read_ready(HANDLE);
|
||||
|
||||
/// The pipe server
|
||||
void pipeServer();
|
||||
#endif
|
||||
|
||||
private:
|
||||
/// the filename of the in pipe
|
||||
@ -65,6 +84,7 @@ private:
|
||||
/// Close pipes
|
||||
void closeConnection();
|
||||
|
||||
#ifndef _WIN32
|
||||
/// start a pipe
|
||||
int startPipe(std::string const &, bool);
|
||||
|
||||
@ -76,6 +96,9 @@ private:
|
||||
|
||||
/// This is -1 if not open
|
||||
int outfd_;
|
||||
#else
|
||||
HANDLE outpipe_;
|
||||
#endif
|
||||
|
||||
/// Are we up and running?
|
||||
bool ready_;
|
||||
@ -88,6 +111,17 @@ private:
|
||||
|
||||
/// The client callback function
|
||||
ClientCallbackfct clientcb_;
|
||||
|
||||
#ifdef _WIN32
|
||||
/// Catch pipe ready-to-be-read notification
|
||||
bool event(QEvent *);
|
||||
|
||||
/// Check whether the pipe server must be stopped
|
||||
BOOL checkStopServerEvent();
|
||||
|
||||
/// Windows event for stopping the pipe server
|
||||
HANDLE stopserver_;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user