// jmode (XIM Server) // connection management #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "xim.h" #include "xdispatch.h" #include #include #include #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 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 p(comWin, xc); gXConnections.insert(p); } static void reapOldConnection() { std::map::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::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::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: */