Mercurial > MadButterfly
diff src/dfb_supp.c @ 903:4c7f0ec97ad9
Copy src/dfb_supp.c from src/X_supp.c
author | Shih-Yuan Lee (FourDollars) <fourdollars@gmail.com> |
---|---|
date | Sun, 03 Oct 2010 16:54:26 +0800 |
parents | src/X_supp.c@512204bcafba |
children | f838e5207ec4 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dfb_supp.c Sun Oct 03 16:54:26 2010 +0800 @@ -0,0 +1,1011 @@ +// -*- 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); +} + +/* @} */