lyx_mirror/src/Server.h
Enrico Forestieri 6ffeab01b7 Implement the LyXServer on Windows.
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/branches/BRANCH_1_6_X@31389 a592a061-630c-0410-9148-cb99ea01b6c8
2009-09-13 20:49:30 +00:00

234 lines
4.8 KiB
C++

// -*- C++ -*-
/**
* \file Server.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Lars Gullik Bjønnes
* \author Jean-Marc Lasgouttes
* \author Enrico Forestieri
*
* Full author contact details are available in file CREDITS.
*/
#ifndef SERVER_H
#define SERVER_H
#include <boost/signals/trackable.hpp>
#ifdef _WIN32
#include <windows.h>
#include <QObject>
#include <QEvent>
#endif
namespace lyx {
class LyXFunc;
class Server;
/** This class managed the pipes used for communicating with clients.
Usage: Initialize with pipe-filename-base, client class to receive
messages, and callback-function that will be called with the messages.
When you want to send, use "send()".
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
cleaner with a Signal/Slot thing.
*/
typedef void (*ClientCallbackfct)(Server *, std::string const &);
/// Construct with pipe-basename and callback to receive messages
LyXComm(std::string const & pip, Server * cli, ClientCallbackfct ccb = 0);
///
~LyXComm() { closeConnection(); }
/// clean up in emergency
void emergencyCleanup();
/// Send message
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
std::string const inPipeName() const;
/// the filename of the out pipe
std::string const outPipeName() const;
/// Open pipes
void openConnection();
/// Close pipes
void closeConnection();
#ifndef _WIN32
/// start a pipe
int startPipe(std::string const &, bool);
/// finish a pipe
void endPipe(int &, std::string const &, bool);
/// This is -1 if not open
int infd_;
/// 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_;
/// Base of pipename including path
std::string pipename_;
/// The client
Server * client_;
/// The client callback function
ClientCallbackfct clientcb_;
};
///
class Server {
public:
// FIXME IN 0.13
// Hack! This should be changed in 0.13
// The lyx server should not take an argument "LyXFunc" but this is
// how it will be done for 0.12. In 0.13 we must write a non-gui
// bufferview.
// IMO lyxserver is atypical, and for the moment the only one, non-gui
// bufferview. We just have to find a way to handle situations like if
// lyxserver is using a buffer that is being edited with a bufferview.
// With a common buffer list this is not a problem, maybe. (Alejandro)
///
Server(LyXFunc * f, std::string const & pip);
///
~Server();
///
void notifyClient(std::string const &);
/// whilst crashing etc.
void emergencyCleanup() { pipes_.emergencyCleanup(); }
///
void callback(std::string const & msg);
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];
///
int numclients_;
///
LyXFunc * func_;
///
LyXComm pipes_;
};
/// Implementation is in LyX.cpp
extern Server & theServer();
} // namespace lyx
#endif // SERVER_H