// XIM プロトコルで定義されるIMの処理を行う。

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <map>
#include "xim.h"
#include <X11/Xutil.h>
#define NEED_EVENTS	// xEventの宣言のため、
#include <X11/Xproto.h>

#ifndef __GNUC__
# ifdef HAVE_ALLOCA_H
#  include <alloca.h>
# endif
#endif

static std::map<int,XimIM *> g_ims;

class XimIM_impl : public XimIM {
public:
    XimIM_impl(Connection *c,int id);
    virtual ~XimIM_impl();
    void create_ic(RxPacket *);
    void destroy_ic(int );
    void set_ic_focus(int icid);
    void set_ic_values(RxPacket *, int icid);
    void get_ic_values(RxPacket *);
    void unset_ic_focus(int icid);
    void forward_event(RxPacket *);
    XimIC *get_ic_by_id(int id);
    void onSendPacket();

private:
    int unused_ic_id();
    void free_all_ic();
    void delete_ic(XimIC *);

    std::map<int,XimIC *> m_ics;
};

XimIM_impl::XimIM_impl(Connection *c,int id) : XimIM(c,id)
{
}

XimIM_impl::~XimIM_impl()
{
    if (g_option_mask & OPT_TRACE) {
	printf("imid=%d im deleted.\n", mID);
    }
    free_all_ic();
}

void XimIM_impl::create_ic(RxPacket *p)
{
    XimIC *ic;
    int icid= unused_ic_id();
    ic = ::create_ic(mConn,p,mID,icid);
    if (!ic) {
	mConn->push_error_packet(mID,icid,
				 ERR_Style,"invalid im style");
	mConn->terminate();
	return ;
    }
    std::pair<int,XimIC *> n(ic->get_icid(),ic);
    m_ics.insert(n);

    TxPacket *t;
    t = createTxPacket(XIM_CREATE_IC_REPLY,0);
    t->pushC16(mID);
    t->pushC16(ic->get_icid());
    mConn->push_packet(t);
}

void XimIM_impl::destroy_ic(int icid)
{
    //ICを破壊
    XimIC *ic = get_ic_by_id(icid);
    delete_ic(ic);
}

void XimIM_impl::set_ic_values(RxPacket *p, int icid)
{
    // imid, icid
    p->rewind();
    (void)p->getC16();
    (void)p->getC16();

    XimIC *ic = get_ic_by_id(icid);

    int atr_len;
    atr_len = p->getC16();
    p->getC16();

    unsigned char *v;
    v = (unsigned char *)alloca(atr_len);

    int i;
    for ( i = 0 ; i < atr_len ; i++){
	v[i] = p->getC8();
    }

    if (ic) {
	ic->set_ic_attrs((void *)v, atr_len);
    }
}

void XimIM_impl::get_ic_values(RxPacket *p)
{
    int icid;
    XimIC *ic;
    p->rewind();
    p->getC16();
    icid = p->getC16();
    ic = get_ic_by_id(icid);

    int len;
    len = p->getC16();

    TxPacket *t = createTxPacket(XIM_GET_IC_VALUES_REPLY,0);
    t->pushC16(mID);
    t->pushC16(icid);
    int i,id,l;
    l = 0;
    for (i = 0; i < len /2 ; i++) {
	id = p->getC16();
	l += ic->get_ic_atr(id, 0);
	l += 4;
    }
    t->pushC16(l);
    t->pushC16(0);

    p->rewind();
    p->getC16();//imid
    p->getC16();//icid
    p->getC16();// n

    for (i = 0; i < len /2 ; i++) {
	id = p->getC16();
	t->pushC16(id);
	t->pushC16(ic->get_ic_atr(id, 0));
	l += ic->get_ic_atr(id, t);
    }
    mConn->push_packet(t);
}

int XimIM_impl::unused_ic_id()
{
    std::map<int,XimIC *>::iterator i;
    int max_id=1; // input-context IDは1から使う?
    for (i = m_ics.begin(); i != m_ics.end(); i++) {
	if (max_id <= (*i).first) {
	    max_id = (*i).first+1;
	}
    }
    return max_id;
}

void XimIM_impl::set_ic_focus(int icid)
{
    XimIC *ic = get_ic_by_id(icid);
    if (ic) {
	ic->set_focus();
    }
}

void XimIM_impl::unset_ic_focus(int icid)
{
    XimIC *ic = get_ic_by_id(icid);
    if (ic) {
	ic->unset_focus();
    }
}

XimIC *XimIM_impl::get_ic_by_id(int icid)
{
    std::map<int,XimIC *>::iterator it;
    it = m_ics.find(icid);
    if (it == m_ics.end()) {
	return 0;
    }
    return it->second;
}

void XimIM_impl::forward_event(RxPacket *p)
{
    unsigned char *c;
    int i;
    keyEventX k;
    xEvent ev_raw;

    int imid,icid,flag;

    XimIC *ic;
    imid = p->getC16();
    icid = p->getC16();
    flag = p->getC16();
    ic = get_ic_by_id(icid);
    k.serial = p->getC16();

    //xEventのコピーを取る。
    c = (unsigned char *)&ev_raw;
    for (i = 0 ; i < (int)sizeof(ev_raw); i++) {
	*c = p->getC8();
	c++;
    }
    
    k.ev.type = ev_raw.u.u.type &0x7f;
    k.ev.xany.serial = (k.serial << 16)| ev_raw.u.u.sequenceNumber;
    k.ev.xany.display = gDpy;
    k.ev.xany.send_event = ev_raw.u.u.type>127;

    switch (k.ev.type) {
    case KeyPress:
	//case KeyRelease:
	k.ev.xkey.window =
	    mConn->to_hl(ev_raw.u.keyButtonPointer.event);
	k.ev.xkey.root =
	    mConn->to_hl(ev_raw.u.keyButtonPointer.root);
	k.ev.xkey.subwindow =
	    mConn->to_hl(ev_raw.u.keyButtonPointer.child);
	k.ev.xkey.time =
	    mConn->to_hs(ev_raw.u.keyButtonPointer.time);
	k.ev.xkey.x =
	    mConn->to_hl(ev_raw.u.keyButtonPointer.eventX);
	k.ev.xkey.y =
	    mConn->to_hs(ev_raw.u.keyButtonPointer.eventY);
	k.ev.xkey.x_root =
	    mConn->to_hs(ev_raw.u.keyButtonPointer.rootX);
	k.ev.xkey.y_root =
	    mConn->to_hs(ev_raw.u.keyButtonPointer.rootY);
	k.ev.xkey.state = ev_raw.u.keyButtonPointer.state;
	k.ev.xkey.keycode = ev_raw.u.u.detail;
	k.ev.xkey.same_screen = ev_raw.u.keyButtonPointer.sameScreen;
	char  buf[10];
	KeySym ks;
	XLookupString(&k.ev.xkey,buf,10,&ks,0);
	k.state = mConn->to_hs(ev_raw.u.keyButtonPointer.state);
	k.press = (k.ev.type == KeyPress);
	k.key_sym = ks;
	if (ic) {
	    ic->OnKeyEvent(k);
	}
	break;
    default:
	printf("unknown type of forwarded event.(%d)\n", k.ev.type);
	break;
    }
}

void XimIM_impl::free_all_ic()
{
    std::map<int,XimIC *>::iterator i;
    for ( i = m_ics.begin() ; i!= m_ics.end() ; i++){
	(*i).second->unset_focus();
	delete (*i).second;
    }
    m_ics.erase(m_ics.begin(),m_ics.end());
}

void XimIM_impl::delete_ic(XimIC *ic)
{
    std::map<int,XimIC *>::iterator it;
    for (it = m_ics.begin(); it != m_ics.end(); it++) {
	if (it->second == ic) {
	    it->second->unset_focus();
	    delete it->second;
	    m_ics.erase(it);
	    return ;
	}
    }
}

void XimIM_impl::onSendPacket()
{
    std::map<int,XimIC *>::iterator i;
    for ( i = m_ics.begin() ; i!= m_ics.end() ; i++){
	(*i).second->onSendPacket();
    }
}

XimIM::XimIM(Connection *c,int id)
{
    mConn = c;
    mID = id;
}

int unused_im_id()
{
    int max_id;
    std::map<int,XimIM *>::iterator i;
    max_id = 1;
    for ( i = g_ims.begin() ; i != g_ims.end() ; i++){
	if ( (*i).first == max_id ){
	    max_id = (*i).first +1;
	}
    }
    return max_id;
}

XimIM *create_im(Connection *c,int id)
{
    XimIM *im;
    im = new XimIM_impl(c,id);
    std::pair<int , XimIM *> p(id,im);
    g_ims.insert(p);
    return im;
}

XimIM *get_im_by_id(int id)
{
    std::map<int,XimIM *>::iterator it;
    it = g_ims.find(id);
    if (it == g_ims.end()) {
	return NULL;
    }
    return it->second;
}

void close_im(int id)
{
    XimIM *im;

    im = get_im_by_id(id);
    if (im) {
	delete im;
    }

    std::map<int,XimIM *>::iterator it;
    it = g_ims.find(id);
    if (it != g_ims.end()) {
	g_ims.erase(it);
    }
}
/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1