view src/X_supp.c @ 880:ac3e8492ad74 abs_n_rel_center

Formalize path data for MadButterfly. Inkscape and other editors would omit 'l' or 'L' after 'm' or 'M'. MadButterfly can not handle it, now. So, we work around it at SVG parser.
author Thinker K.F. Li <thinker@codemud.net>
date Sat, 25 Sep 2010 18:46:37 +0800
parents 512204bcafba
children 3fe8054457a8
line wrap: on
line source

// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
// vim: sw=4:ts=8:sts=4
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <cairo-xlib.h>
#include "mb_graph_engine.h"
#include "mb_redraw_man.h"
#include "mb_timer.h"
#include "mb_X_supp.h"
#include "config.h"

#ifdef XSHM
/* \sa http://www.xfree86.org/current/mit-shm.html */
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
#endif

#define ERR -1
#define OK 0

#define ONLY_MOUSE_MOVE_RAW 1

/*! \ingroup xkb
 * @{
 */
struct _X_kb_info {
    int keycode_min, keycode_max;
    int ksym_per_code;
    KeySym *syms;
    subject_t *kbevents;
    ob_factory_t *ob_factory;
};

/* @} */
#define MAX_MONITORS 200
typedef struct {
    int type;
    int fd;
    mb_eventcb_t f;
    void *arg;
}  monitor_t;

struct _X_MB_runtime {
    Display *display;
    Window win;
    Visual *visual;
    mbe_surface_t *surface, *backend_surface;
    mbe_pattern_t *surface_ptn;
    mbe_t *cr, *backend_cr;
    redraw_man_t *rdman;
    mb_tman_t *tman;
    mb_img_ldr_t *img_ldr;
    int w, h;

    X_kb_info_t kbinfo;
    monitor_t monitors[MAX_MONITORS];
    int n_monitor;

#ifndef ONLY_MOUSE_MOVE_RAW
    /* States */
    shape_t *last;
#endif

#ifdef XSHM
    XImage *ximage;
    XShmSegmentInfo shminfo;
#endif

    /*
     * Following variables are used by handle_single_x_event()
     */
    int last_evt_type;	       /* Type of last event */
    int eflag;
    int ex1, ey1, ex2, ey2;    /* Aggregate expose events */
    int mflag;
    int mx, my;		       /* Position of last motion event */
    int mbut_state;	       /* Button state of last motion event */
};

#ifdef XSHM
static void
XSHM_update(X_MB_runtime_t *xmb_rt) {
    GC gc;

    gc = DefaultGC(xmb_rt->display, DefaultScreen(xmb_rt->display));
    if(xmb_rt->ximage) {	/* support XSHM */
	XShmPutImage(xmb_rt->display,
		     xmb_rt->win,
		     gc,
		     xmb_rt->ximage,
		     0, 0, 0, 0,
		     xmb_rt->w, xmb_rt->h, 0);
    }
}
#endif

/*! \defgroup xkb X Keyboard Handling
 *
 * Accept keyboard events from X server and delivery it to
 * application through observer pattern.  There is a subject,
 * per X-connection, for that.
 * @{
 */
static int keycode2sym(X_kb_info_t *kbinfo, unsigned int keycode) {
    int sym_idx;
    int sym;

    sym_idx = kbinfo->ksym_per_code * (keycode - kbinfo->keycode_min);
    sym =  kbinfo->syms[sym_idx];
    return sym;
}

static int X_kb_init(X_kb_info_t *kbinfo, Display *display,
		     redraw_man_t *rdman) {
    int n_syms;
    ob_factory_t *factory;
    int r;

    r = XDisplayKeycodes(display,
			 &kbinfo->keycode_min,
			 &kbinfo->keycode_max);
    if(r == 0)
	return ERR;

    n_syms = kbinfo->keycode_max - kbinfo->keycode_min + 1;
    kbinfo->syms = XGetKeyboardMapping(display, kbinfo->keycode_min,
				       n_syms,
				       &kbinfo->ksym_per_code);
    if(kbinfo->syms == NULL)
	return ERR;

    factory = rdman_get_ob_factory(rdman);
    kbinfo->kbevents = subject_new(factory, kbinfo, OBJT_KB);
    if(kbinfo->kbevents == NULL)
	return ERR;
    /*! \todo Make sure ob_factory is still need. */
    kbinfo->ob_factory = factory;

    return OK;
}

static void X_kb_destroy(X_kb_info_t *kbinfo) {
    subject_free(kbinfo->kbevents);
    XFree(kbinfo->syms);
}

/*! \brief Accept X keyboard events from handle_x_event() and dispatch it.
 */
static void X_kb_handle_event(X_kb_info_t *kbinfo, XKeyEvent *xkey) {
    unsigned int code;
    int sym;
    X_kb_event_t event;

    code = xkey->keycode;
    sym = keycode2sym(kbinfo, code);
    if(xkey->type == KeyPress)
	event.event.type = EVT_KB_PRESS;
    else if(xkey->type == KeyRelease)
	event.event.type = EVT_KB_RELEASE;
    event.event.tgt = event.event.cur_tgt = kbinfo->kbevents;
    event.keycode = code;
    event.sym = sym;

    subject_notify(kbinfo->kbevents, &event.event);
}

/* @} */

static unsigned int get_button_state(unsigned int state) {
    unsigned int but = 0;

    if(state & Button1Mask)
	but |= MOUSE_BUT1;
    if(state & Button2Mask)
	but |= MOUSE_BUT2;
    if(state & Button3Mask)
	but |= MOUSE_BUT3;

    return but;
}

static unsigned int get_button(unsigned int button) {
    switch(button) {
    case Button1:
	return MOUSE_BUT1;
    case Button2:
	return MOUSE_BUT2;
    case Button3:
	return MOUSE_BUT3;
    }
    return 0;
}

/*! \brief Notify observers of the shape at specified
 *	position for mouse event.
 *
 * Observers of parent shapes may be called if the subject is not
 * with SUBF_STOP_PROPAGATE flag.  The subject of mouse event
 * for a shape is returned by sh_get_mouse_event_subject().
 */
static void notify_coord_or_shape(redraw_man_t *rdman,
				   mb_obj_t *obj,
				   co_aix x, co_aix y, int etype,
				   unsigned int state,
				   unsigned int button) {
    mouse_event_t mouse_event;
    subject_t *subject;

    mouse_event.event.type = etype;
    mouse_event.x = x;
    mouse_event.y = y;
    mouse_event.but_state = state;
    mouse_event.button = button;

    if(IS_MBO_SHAPES(obj))
	subject = sh_get_mouse_event_subject((shape_t *)obj);
    else
	subject = coord_get_mouse_event((coord_t *)obj);

    subject_notify(subject, (event_t *)&mouse_event);
}

/*! \brief Handle motion event.
 */
static void
handle_motion_event(X_MB_runtime_t *rt) {
    redraw_man_t *rdman = rt->rdman;
    int x, y;
    int state;
    shape_t *shape;
    coord_t *root;
    int in_stroke;
    
    x = rt->mx;
    y = rt->my;
    state = rt->mbut_state;
    
    shape = find_shape_at_pos(rdman, x, y,
			      &in_stroke);
#ifdef ONLY_MOUSE_MOVE_RAW
    if(shape != NULL) {
	notify_coord_or_shape(rdman, (mb_obj_t *)shape,
			      x, y, EVT_MOUSE_MOVE_RAW, state, 0);
    } else {
	root = rdman_get_root(rdman);
	notify_coord_or_shape(rdman, (mb_obj_t *)root,
			      x, y, EVT_MOUSE_MOVE_RAW, state, 0);
    }
#else
    if(shape != NULL) {
	if(rt->last != shape) {
	    if(rt->last)
		notify_coord_or_shape(rdman, rt->last, x, y,
				      EVT_MOUSE_OUT, state, 0);
	    notify_coord_or_shape(rdman, shape, x, y,
				  EVT_MOUSE_OVER, state, 0);
	    rt->last = shape;
	} else
	    notify_coord_or_shape(rdman, shape, x, y,
				  EVT_MOUSE_MOVE, state, 0);
    } else {
	if(rt->last) {
	    notify_coord_or_shape(rdman, rt->last, x, y,
				  EVT_MOUSE_OUT, state, 0);
	    rt->last = NULL;
	}
    }
#endif
    
    rt->mflag = 0;
}

/*! \brief Redraw exposed area.
 */
static void
handle_expose_event(X_MB_runtime_t *rt) {
    redraw_man_t *rdman = rt->rdman;
    int ex1, ey1, ex2, ey2;

    ex1 = rt->ex1;
    ey1 = rt->ey1;
    ex2 = rt->ex2;
    ey2 = rt->ey2;
    
    rdman_redraw_area(rdman, ex1, ey1, (ex2 - ex1), (ey2 - ey1));
    
    rt->eflag = 0;
}

/*! \brief Handle single X event and maintain internal states.
 *
 * It keeps internal state in rt to improve performance.
 */
static void
handle_single_x_event(X_MB_runtime_t *rt, XEvent *evt) {
    redraw_man_t *rdman = rt->rdman;
    XMotionEvent *mevt;
    XButtonEvent *bevt;
    XExposeEvent *eevt;
    XKeyEvent *xkey;
    int x, y, w, h;

    shape_t *shape;

    unsigned int state, button;
    int in_stroke;

    if(evt->type != MotionNotify && rt->mflag)
	handle_motion_event(rt);

    switch(evt->type) {
    case ButtonPress:
	bevt = (XButtonEvent *)evt;
	x = bevt->x;
	y = bevt->y;
	state = get_button_state(bevt->state);
	button = get_button(bevt->button);

	shape = find_shape_at_pos(rdman, x, y,
				  &in_stroke);
	if(shape)
	    notify_coord_or_shape(rdman, (mb_obj_t *)shape,
				  x, y, EVT_MOUSE_BUT_PRESS,
				  state, button);
	break;

    case ButtonRelease:
	bevt = (XButtonEvent *)evt;
	x = bevt->x;
	y = bevt->y;
	state = get_button_state(bevt->state);
	button = get_button(bevt->button);

	shape = find_shape_at_pos(rdman, x, y,
				  &in_stroke);
	if(shape)
	    notify_coord_or_shape(rdman, (mb_obj_t *)shape,
				  x, y, EVT_MOUSE_BUT_RELEASE,
				  state, button);
	break;

    case MotionNotify:
	mevt = (XMotionEvent *)evt;
	rt->mx = mevt->x;
	rt->my = mevt->y;
	rt->mbut_state = get_button_state(mevt->state);
	rt->mflag = 1;
	break;

    case KeyPress:
    case KeyRelease:
	xkey = &evt->xkey;
	X_kb_handle_event(&rt->kbinfo, xkey);
	break;

    case Expose:
	eevt = &evt->xexpose;
	x = eevt->x;
	y = eevt->y;
	w = eevt->width;
	h = eevt->height;

	if(rt->eflag) {
	    if(x < rt->ex1)
		rt->ex1 = x;
	    if(y < rt->ey1)
		rt->ey1 = y;
	    if((x + w) > rt->ex2)
		rt->ex2 = x + w;
	    if((y + h) > rt->ey2)
		rt->ey2 = y + h;
	} else {
	    rt->ex1 = x;
	    rt->ey1 = y;
	    rt->ex2 = x + w;
	    rt->ey2 = y + h;
	    rt->eflag = 1;
	}
	break;
    }
}

/*! \brief Call when no more event in an event iteration.
 *
 * No more event means event queue is emplty.  This function will
 * perform some actions according current internal state.
 */
static void
no_more_event(X_MB_runtime_t *rt) {
    if(rt->mflag)
	handle_motion_event(rt);
    if(rt->eflag)
	handle_expose_event(rt);
}

/*! \brief Dispatch all X events in the queue.
 */
static void handle_x_event(X_MB_runtime_t *rt) {
    Display *display = rt->display;
    XEvent evt;
    int r;

    /* XXX: For some unknown reason, it causes a segmentation fault to
     *      called XEventsQueued() after receiving first Expose event
     *      and before redraw for the event.
     */
    while(XEventsQueued(display, QueuedAfterReading) > 0) {
	r = XNextEvent(display, &evt);
	if(r == -1)
	    break;

	handle_single_x_event(rt, &evt);
    }
    no_more_event(rt);
    
#ifdef XSHM
    XSHM_update(rt);
#endif
    XFlush(display);
}

/*! \brief Handle connection coming data and timeout of timers.
 *
 * \param display is a Display returned by XOpenDisplay().
 * \param rdman is a redraw manager.
 * \param tman is a timer manager.
 *
 * The display is managed by specified rdman and tman.  rdman draws
 * on the display, and tman trigger actions according timers.
 */
void X_MB_handle_connection(void *be) {
    X_MB_runtime_t *rt = (X_MB_runtime_t *) be;
    Display *display = rt->display;
    redraw_man_t *rdman = rt->rdman;
    mb_tman_t *tman = rt->tman;
    int fd;
    mb_timeval_t now, tmo;
    struct timeval tv;
    fd_set rfds,wfds;
    int nfds;
    int r, r1,i;

    handle_x_event(rt);

    fd = XConnectionNumber(display);
    nfds = fd + 1;
    while(1) {
	FD_ZERO(&rfds);
	FD_ZERO(&wfds);
	FD_SET(fd, &rfds);
        for(i=0;i<rt->n_monitor;i++) {
	    if (rt->monitors[i].type == MONITOR_READ)
		FD_SET(rt->monitors[i].fd, &rfds);
	    else if (rt->monitors[i].type == MONITOR_WRITE)
		FD_SET(rt->monitors[i].fd, &wfds);
        }

	get_now(&now);
	r = mb_tman_next_timeout(tman, &now, &tmo);

	if(r == 0) {
	    tv.tv_sec = MB_TIMEVAL_SEC(&tmo);
	    tv.tv_usec = MB_TIMEVAL_USEC(&tmo);
	    r1 = select(nfds, &rfds, NULL, NULL, &tv);
	} else
	    r1 = select(nfds, &rfds, NULL, NULL, NULL);

	if(r1 == -1) {
	    perror("select");
	    break;
	}

	if(r1 == 0) {
	    get_now(&now);
	    mb_tman_handle_timeout(tman, &now);
	    rdman_redraw_changed(rdman);
#ifdef XSHM
	    XSHM_update(rt);
#endif
	    XFlush(display);
	} else if(FD_ISSET(fd, &rfds)){
	    handle_x_event(rt);
	} else {
            for(i=0;i<rt->n_monitor;i++) {
	        if (rt->monitors[i].type == MONITOR_READ) {
		    if (FD_ISSET(rt->monitors[i].fd, &rfds))
		    	rt->monitors[i].f(rt->monitors[i].fd,rt->monitors[i].arg);
		} else if (rt->monitors[i].type == MONITOR_WRITE) {
		    if (FD_ISSET(rt->monitors[i].fd, &wfds))
			rt->monitors[i].f(rt->monitors[i].fd,rt->monitors[i].arg);
		}
            }
	}
    }
}

#define ERR -1
#define OK 0

static int X_init_connection(const char *display_name,
			     int w, int h,
			     Display **displayp,
			     Visual **visualp,
			     Window *winp) {
    Display *display;
    Window root, win;
    Visual *visual;
    int screen;
    XSetWindowAttributes wattr;
    int depth;
    int x, y;
    int draw_root = 0;
    const char *disp_name;
    char disp_buf[32];
    int cp;
    int r;

    /*
     * Support drawing on the root window.
     */
    disp_name = display_name;
    if(strstr(display_name, ":root") != NULL) {
	draw_root = 1;
	cp = strlen(display_name) - 5;
	if(cp >= 32)
	    cp = 31;
	memcpy(disp_buf, display_name, cp);
	disp_buf[cp] = 0;
	disp_name = disp_buf;
    }

    display = XOpenDisplay(disp_name);
    if(display == NULL)
	return ERR;

    screen = DefaultScreen(display);
    root = DefaultRootWindow(display);
    visual = DefaultVisual(display, screen);
    depth = DefaultDepth(display, screen);
    wattr.override_redirect = False;
    x = 10;
    y = 10;
    if(draw_root)
	win = RootWindowOfScreen(ScreenOfDisplay(display, screen));
    else {
	win = XCreateWindow(display, root,
			    x, y,
			    w, h,
			    1, depth, InputOutput, visual,
			    CWOverrideRedirect, &wattr);
	r = XMapWindow(display, win);
	if(r == -1) {
	    XCloseDisplay(display);
	    return ERR;
	}
    }

    XSelectInput(display, win, PointerMotionMask | ExposureMask |
		 ButtonPressMask | ButtonReleaseMask |
		 KeyPressMask | KeyReleaseMask);
    XFlush(display);

    *displayp = display;
    *visualp = visual;
    *winp = win;

    return OK;
}

#ifdef XSHM
static void
xshm_destroy(X_MB_runtime_t *xmb_rt) {
    XShmSegmentInfo *shminfo;

    shminfo = &xmb_rt->shminfo;

    if(xmb_rt->shminfo.shmaddr) {
	XShmDetach(xmb_rt->display, shminfo);
    }

    if(xmb_rt->ximage) {
	XDestroyImage(xmb_rt->ximage);
	xmb_rt->ximage = NULL;
    }

    if(shminfo->shmaddr) {
	shmdt(shminfo->shmaddr);
	shminfo->shmaddr = NULL;
    }

    if(shminfo->shmid) {
	shmctl(shminfo->shmid, IPC_RMID, 0);
	shminfo->shmid = 0;
    }
}

static void
xshm_init(X_MB_runtime_t *xmb_rt) {
    Display *display;
    Visual *visual;
    XImage *ximage;
    int screen;
    int depth;
    int support_shm;
    int mem_sz;
    XShmSegmentInfo *shminfo;
    int surf_fmt;

    display = xmb_rt->display;
    visual = xmb_rt->visual;
    shminfo = &xmb_rt->shminfo;

    support_shm = XShmQueryExtension(display);
    if(!support_shm)
	return;

    screen = DefaultScreen(display);
    depth = DefaultDepth(display, screen);

    if(depth != 24 && depth != 32)
	return;

    xmb_rt->ximage = XShmCreateImage(display, visual, depth,
				     ZPixmap, NULL, shminfo,
				     xmb_rt->w, xmb_rt->h);
    ximage = xmb_rt->ximage;

    mem_sz = ximage->bytes_per_line * ximage->height;
    shminfo->shmid = shmget(IPC_PRIVATE, mem_sz, IPC_CREAT | 0777);
    if(shminfo->shmid == -1) {
	xshm_destroy(xmb_rt);
	return;
    }

    shminfo->shmaddr = shmat(shminfo->shmid, 0, 0);
    ximage->data = shminfo->shmaddr;

    shminfo->readOnly = 0;

    XShmAttach(display, shminfo);

    switch(depth) {
    case 24: surf_fmt = CAIRO_FORMAT_RGB24; break;
    case 32: surf_fmt = CAIRO_FORMAT_ARGB32; break;
    }

    xmb_rt->backend_surface =
	mbe_image_surface_create_for_data((unsigned char *)ximage->data,
					  surf_fmt,
					  xmb_rt->w,
					  xmb_rt->h,
					  ximage->bytes_per_line);
    if(xmb_rt->backend_surface == NULL)
	xshm_destroy(xmb_rt);
}
#endif /* XSHM */

/*! \brief Initialize a MadButterfy runtime for Xlib.
 *
 * This one is very like X_MB_init(), except it accepts a
 * X_MB_runtime_t object initialized with a display connected to a X
 * server and an opened window.
 *
 * Following field of the X_MB_runtime_t object should be initialized.
 *   - w, h
 *   - win
 *   - display
 *   - visual
 */
static int
X_MB_init_with_win_internal(X_MB_runtime_t *xmb_rt) {
    mb_img_ldr_t *img_ldr;
    int w, h;

    w = xmb_rt->w;
    h = xmb_rt->h;

#ifdef XSHM
    xshm_init(xmb_rt);
#endif

    xmb_rt->surface =
	mbe_image_surface_create(MB_IFMT_ARGB32, w, h);

    xmb_rt->surface_ptn =
	mbe_pattern_create_for_surface(xmb_rt->surface);

    if(xmb_rt->backend_surface == NULL) /* xshm_init() may create one */
	xmb_rt->backend_surface =
	    mbe_xlib_surface_create(xmb_rt->display,
				    xmb_rt->win,
				    xmb_rt->visual,
				    w, h);

    xmb_rt->cr = mbe_create(xmb_rt->surface);
    xmb_rt->backend_cr = mbe_create(xmb_rt->backend_surface);

    mbe_set_source(xmb_rt->backend_cr, xmb_rt->surface_ptn);

    xmb_rt->rdman = (redraw_man_t *)malloc(sizeof(redraw_man_t));
    redraw_man_init(xmb_rt->rdman, xmb_rt->cr, xmb_rt->backend_cr);
    // FIXME: This is a wired loopback reference. This is inly required when we need
    //        to get the xmb_rt->tman for the animation. We should relocate the tman
    //	      to the redraw_man_t instead.
    xmb_rt->rdman->rt = xmb_rt;

    xmb_rt->tman = mb_tman_new();

    img_ldr = simple_mb_img_ldr_new("");
    xmb_rt->img_ldr = img_ldr;
    rdman_set_img_ldr(xmb_rt->rdman, img_ldr);
    memset(xmb_rt->monitors,0,sizeof(xmb_rt->monitors));

#ifndef ONLY_MOUSE_MOVE_RAW
    xmb_rt->last = NULL;
#endif

    X_kb_init(&xmb_rt->kbinfo, xmb_rt->display, xmb_rt->rdman);

    return OK;
}

/*! \brief Initialize a MadButterfy runtime for Xlib.
 *
 * It setups a runtime environment to run MadButterfly with Xlib.
 * Users should specify width and height of the opening window.
 */
static int X_MB_init(X_MB_runtime_t *xmb_rt, const char *display_name,
		     int w, int h) {
    int r;

    memset(xmb_rt, 0, sizeof(X_MB_runtime_t));

    xmb_rt->w = w;
    xmb_rt->h = h;
    r = X_init_connection(display_name, w, h, &xmb_rt->display,
			  &xmb_rt->visual, &xmb_rt->win);
    if(r != OK)
	return ERR;

    r = X_MB_init_with_win_internal(xmb_rt);

    return r;
}

/*! \brief Initialize a MadButterfly runtime for a window of X.
 *
 * Runtimes initialized with this function should be destroyed with
 * X_MB_destroy_keep_win().
 */
static int
X_MB_init_with_win(X_MB_runtime_t *xmb_rt,
		   Display *display, Window win) {
    XWindowAttributes attrs;
    int r;

    r = XGetWindowAttributes(display, win, &attrs);
    if(r == 0)
	return ERR;
    
    memset(xmb_rt, 0, sizeof(X_MB_runtime_t));

    xmb_rt->display = display;
    xmb_rt->win = win;
    xmb_rt->visual = attrs.visual;
    xmb_rt->w = attrs.width;
    xmb_rt->h = attrs.height;

    r = X_MB_init_with_win_internal(xmb_rt);

    return r;
}

static void X_MB_destroy(X_MB_runtime_t *xmb_rt) {
    if(xmb_rt->rdman) {
	redraw_man_destroy(xmb_rt->rdman);
	free(xmb_rt->rdman);
    }

    if(xmb_rt->tman)
	mb_tman_free(xmb_rt->tman);

    if(xmb_rt->img_ldr)
	MB_IMG_LDR_FREE(xmb_rt->img_ldr);

    if(xmb_rt->cr)
	mbe_destroy(xmb_rt->cr);
    if(xmb_rt->backend_cr)
	mbe_destroy(xmb_rt->backend_cr);

    if(xmb_rt->surface)
	mbe_surface_destroy(xmb_rt->surface);
    if(xmb_rt->surface_ptn)
	mbe_pattern_destroy(xmb_rt->surface_ptn);
    if(xmb_rt->backend_surface)
	mbe_surface_destroy(xmb_rt->backend_surface);

    if(xmb_rt->display)
	XCloseDisplay(xmb_rt->display);

    X_kb_destroy(&xmb_rt->kbinfo);
}

/*! \brief Destroy a MadButterfly runtime initialized with
 *	X_MB_init_with_win().
 *
 * Destroying a runtime with this function prevent the window and
 * display associated with the runtime being closed.
 */
static void
X_MB_destroy_keep_win(X_MB_runtime_t *xmb_rt) {
    Display *display;
    Window win;

    display = xmb_rt->display;
    xmb_rt->display = NULL;
    win = xmb_rt->win;
    xmb_rt->win = 0;

    X_MB_destroy(xmb_rt);
    
    xmb_rt->display = display;
    xmb_rt->win = win;
}

void *X_MB_new(const char *display_name, int w, int h) {
    X_MB_runtime_t *rt;
    int r;

    rt = O_ALLOC(X_MB_runtime_t);
    if(rt == NULL)
	return NULL;

    r = X_MB_init(rt, display_name, w, h);
    if(r != OK) {
	free(rt);
	return NULL;
    }

    return rt;
}

/*! \brief Create a new runtime for existed window for X.
 *
 * The object returned by this function must be free with
 * X_MB_free_keep_win() to prevent the window from closed.
 */
void *X_MB_new_with_win(Display *display, Window win) {
    X_MB_runtime_t *rt;
    int r;

    rt = O_ALLOC(X_MB_runtime_t);
    if(rt == NULL)
	return NULL;

    r = X_MB_init_with_win(rt, display, win);
    if(r != OK) {
	free(rt);
	return NULL;
    }

    return rt;
}

void X_MB_free(void *rt) {
    X_MB_destroy((X_MB_runtime_t *) rt);
    free(rt);
}

/*! \brief Free runtime created with X_MB_new_with_win().
 */
void
X_MB_free_keep_win(void *rt) {
    X_MB_destroy_keep_win((X_MB_runtime_t *) rt);
    free(rt);
}

subject_t *X_MB_kbevents(void *rt) {
    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
    return xmb_rt->kbinfo.kbevents;
}

redraw_man_t *X_MB_rdman(void *rt) {
    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
    return xmb_rt->rdman;
}

mb_tman_t *X_MB_tman(void *rt) {
    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
    return xmb_rt->tman;
}

ob_factory_t *X_MB_ob_factory(void *rt) {
    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
    ob_factory_t *factory;

    factory = rdman_get_ob_factory(xmb_rt->rdman);
    return factory;
}

mb_img_ldr_t *X_MB_img_ldr(void *rt) {
    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
    mb_img_ldr_t *img_ldr;

    img_ldr = xmb_rt->img_ldr;

    return img_ldr;
}

void X_MB_add_event(void *rt, int type, int fd, mb_eventcb_t f,void *arg)
{
    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
    int i;

    for(i=0;i<xmb_rt->n_monitor;i++) {
        if (xmb_rt->monitors[i].type == type && xmb_rt->monitors[i].fd == fd) {
	    xmb_rt->monitors[i].f = f;
	    xmb_rt->monitors[i].arg = arg;
	    return;
	}
    }
    for(i=0;i<xmb_rt->n_monitor;i++) {
        if (xmb_rt->monitors[i].type == 0) {
	    xmb_rt->monitors[i].type = type;
	    xmb_rt->monitors[i].fd = fd;
	    xmb_rt->monitors[i].f = f;
	    xmb_rt->monitors[i].arg = arg;
	    return;
	}
    }
    if (i == MAX_MONITORS) return;
    xmb_rt->monitors[i].type = type;
    xmb_rt->monitors[i].fd = fd;
    xmb_rt->monitors[i].f = f;
    xmb_rt->monitors[i].arg = arg;
    i++;
    xmb_rt->n_monitor=i;
}

void X_MB_remove_event(void *rt, int type, int fd)
{
    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *) rt;
    int i;
    for(i=0;i<xmb_rt->n_monitor;i++) {
        if (xmb_rt->monitors[i].type == type && xmb_rt->monitors[i].fd == fd) {
	    xmb_rt->monitors[i].type = 0;
	    return;
	}
    }
}
mb_backend_t backend = { X_MB_new,
			 X_MB_free,
			 X_MB_add_event,
			 X_MB_remove_event,
			 X_MB_handle_connection,
			 X_MB_kbevents,
			 X_MB_rdman,
			 X_MB_tman,
			 X_MB_ob_factory,
			 X_MB_img_ldr
		};
/*! \defgroup x_supp_nodejs_sup Export functions for supporting nodejs plugin.
 *
 * These functions are for internal using.
 * @{
 */
/*! \brief Exported for nodejs plugin to call handle_x_event.
 */
void _X_MB_handle_x_event_for_nodejs(void *rt) {
    handle_x_event((X_MB_runtime_t *)rt);
}

/*! \brief Get X connect for nodejs plugin.
 */
int _X_MB_get_x_conn_for_nodejs(void *rt) {
    return XConnectionNumber(((X_MB_runtime_t *)rt)->display);
}

/*! \brief Flush buffer for the X connection of a runtime object.
 */
int _X_MB_flush_x_conn_for_nodejs(void *rt) {
    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *)rt;
#ifdef XSHM
    XSHM_update(xmb_rt);
#endif
    return XFlush(xmb_rt->display);
}

/*! \brief Handle single X event.
 */
void
_X_MB_handle_single_event(void *rt, void *evt) {
    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *)rt;
    
    handle_single_x_event(xmb_rt, (XEvent *)evt);
}

/*! \brief Called at end of an iteration of X event loop.
 */
void
_X_MB_no_more_event(void *rt) {
    X_MB_runtime_t *xmb_rt = (X_MB_runtime_t *)rt;
    
    no_more_event(xmb_rt);
}

/* @} */