// jmode (XIM Server)
// connection management
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "xim.h"
#include "xdispatch.h"
#include <list>
#include <map>
#include <X11/Xatom.h>
#define BUF_SIZE 1024
#define TRANSPORT_UNIT 20
#define TRANSPORT_MAX 100
class XConnection :
public Connection , public WindowIf
{
public:
XConnection(Window clientWin, Window commWin);
virtual ~XConnection();
virtual void expose(Window){};
virtual void destroy(Window);
void readProc(XClientMessageEvent *);
void writeProc();
bool isValid(){return mIsValid;};
private:
bool readToBuf(XClientMessageEvent *);
bool checkByteorder();
void shiftBuffer(int );
void doSend(TxPacket *t);
Window mClientWin,mCommWin;
bool mIsValid;
struct {
char *buf;
int len;
}mBuf;
};
static std::map<Window,XConnection *> gXConnections;
static Atom xim_xconnect;
static Atom xim_protocol;
static Atom xim_moredata;
static Atom jmode_comm;
static void procNewXConnection(XClientMessageEvent *ev)
{
XClientMessageEvent r;
Window comWin,clientWin;
comWin = XCreateSimpleWindow(gDpy, ev->window,
0, 0, 1, 1, 0, 0, 0);
clientWin = ev->data.l[0];
r.type = ClientMessage;
r.window = clientWin;
r.message_type = xim_xconnect;
r.format = 32;
r.data.l[0] = comWin;
r.data.l[1] = 0;
r.data.l[2] = 2;
r.data.l[3] = TRANSPORT_MAX;
XSendEvent(gDpy, clientWin, False, NoEventMask, (XEvent *)&r);
XConnection *xc = new XConnection(clientWin, comWin);
std::pair<Window,XConnection *> p(comWin, xc);
gXConnections.insert(p);
}
static void reapOldConnection()
{
std::map<Window,XConnection *>::iterator it;
for (it = gXConnections.begin(); it != gXConnections.end(); it ++) {
XConnection *xc = (*it).second;
if (!xc->isValid()) {
delete xc;
gXConnections.erase(it);
return ;
}
}
}
void procXClientMessage(XClientMessageEvent *ev)
{
if (ev->window == gWnd) {
procNewXConnection(ev);
reapOldConnection();
}else{
std::map<Window,XConnection *>::iterator i;
i = gXConnections.find(ev->window);
if (i != gXConnections.end()) {
XConnection *xc = (*i).second;
int error_count = dpy_error_count;
xc->readProc(ev);
if (error_count != dpy_error_count) {
printf("failed to read\n");
xc->terminate();
}
}else {
printf("packet for unknown window\n");
}
}
}
XConnection::XConnection(Window clientWin, Window commWin)
{
mClientWin = clientWin;
mCommWin = commWin;
mBuf.buf = (char *)malloc(BUF_SIZE);
mBuf.len = 0;
add_window_watch(mClientWin, this, STRUCTURE_NOTIFY_MASK);
mIsValid = true;
}
XConnection::~XConnection()
{
XDestroyWindow(gDpy, mCommWin);
free(mBuf.buf);
}
void XConnection::destroy(Window w)
{
if (mIsValid) {
OnClose();
}
mIsValid = false;
}
void XConnection::readProc(XClientMessageEvent *ev)
{
if (!readToBuf(ev)) {
return ;
}
checkByteorder();
bool pushed = true;
do{
int len = -1;
if (mBuf.len >= 4) {
len = RxPacket::getPacketLength((unsigned char *)mBuf.buf,
mByteorder);
}
if ((len > 4 && len <= mBuf.len) ||
(len == 4 && mBuf.buf[0] == XIM_DISCONNECT)) {
RxPacket *p =
createRxPacket((unsigned char *)mBuf.buf, mByteorder);
shiftBuffer(len);
mRxQ.push_back(p);
} else if (len == 4) {
// do nothing
shiftBuffer(4);
} else {
pushed = false;
}
} while(pushed);
OnRecv();
writeProc();
}
void XConnection::shiftBuffer(int len)
{
memmove(mBuf.buf, &mBuf.buf[len], mBuf.len - len);
mBuf.len -= len;
}
bool XConnection::checkByteorder()
{
if (mByteorder == BYTEORDER_UNKNOWN) {
if (mBuf.buf[0] != XIM_CONNECT) {
printf("not XIM_CONNECT\n");
return false;
}
if (mBuf.buf[4] == 0x42) {
mByteorder = MSB_FIRST;
}else if (mBuf.buf[4] == 0x6c) {
mByteorder = LSB_FIRST;
}else{
return false;
}
}
return true;
}
bool XConnection::readToBuf(XClientMessageEvent *ev)
{
if (ev->format == 32 && ev->message_type == xim_protocol) {
// indirect
int offset = 0;
Atom type;
int format;
unsigned long nrItems;
unsigned long remain;
char *data;
do{
XGetWindowProperty(gDpy, ev->window, ev->data.l[1],
offset, BUF_SIZE - mBuf.len,True,
AnyPropertyType,
&type, &format, &nrItems, &remain,
(unsigned char **)&data);
if (!data) {
return false;
}
if (format == 8) {
memcpy(&mBuf.buf[mBuf.len], data, nrItems);
mBuf.len += nrItems;
} else {
return false;
}
XFree(data);
} while (remain > 0);
} else if (ev->format == 8) {
// direct
if (mBuf.len + TRANSPORT_UNIT >= BUF_SIZE) {
return false;
}
memcpy(&mBuf.buf[mBuf.len], ev->data.b, TRANSPORT_UNIT);
mBuf.len += TRANSPORT_UNIT;// XXX may over run
} else {
return false;
}
return true;
}
void XConnection::writeProc()
{
std::list<TxPacket *>::iterator i;
if (!mTxQ.size()) {
return ;
}
OnSend();
int error_count = dpy_error_count;
while(mPTxQ.size()) {
i = mPTxQ.begin();
doSend(*i);
delete *i;
mPTxQ.pop_front();
}
while (mTxQ.size()) {
i = mTxQ.begin();
doSend(*i);
delete *i;
mTxQ.pop_front();
}
XFlush(gDpy);
if (error_count != dpy_error_count) {
printf("failed to write\n");
terminate();
}
if (mIsCloseWait) {
mClientWin = None;
mIsValid = false;
OnClose();
}
}
void XConnection::doSend(TxPacket *t)
{
XClientMessageEvent r;
int buflen;
char *buf;
buflen = t->get_length();
buf = (char *)alloca(buflen);
t->write_to_buf((unsigned char*)buf, buflen, mByteorder);
if (buflen < TRANSPORT_MAX) {
// via event
int offset = 0;
int length = buflen;
while (length > TRANSPORT_UNIT) {
r.type = ClientMessage;
r.window = mClientWin;
r.format = 8;
r.message_type = xim_moredata;
memcpy(r.data.b, &buf[offset], TRANSPORT_UNIT);
XSendEvent(gDpy, mClientWin, False, NoEventMask, (XEvent *)&r);
offset += TRANSPORT_UNIT;
length -= TRANSPORT_UNIT;
}
r.type = ClientMessage;
r.window = mClientWin;
r.format = 8;
r.message_type = xim_protocol;
memset(r.data.b, 0, TRANSPORT_UNIT);
memcpy(r.data.b, &buf[offset], length);
XSendEvent(gDpy, mClientWin, False, NoEventMask, (XEvent *)&r);
} else {
// via property
r.type = ClientMessage;
r.window = mClientWin;
r.format = 32;
r.message_type = xim_protocol;
r.data.l[0] = buflen;
r.data.l[1] = jmode_comm;
XChangeProperty(gDpy, mClientWin, jmode_comm, XA_STRING,
8, PropModeAppend, (unsigned char *)buf, buflen);
XSendEvent(gDpy, mClientWin, False, NoEventMask, (XEvent *)&r);
}
}
int connection_setup()
{
xim_xconnect = XInternAtom(gDpy, "_XIM_XCONNECT", False);
xim_protocol = XInternAtom(gDpy, "_XIM_PROTOCOL", False);
xim_moredata = XInternAtom(gDpy, "_XIM_MOREDATA", False);
jmode_comm = XInternAtom(gDpy, "JMODE_COMM", False);
return 0;
}
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1