view src/cons_supp.c @ 1479:92a8497d0361

Make FSM editor scrollable
author Thinker K.F. Li <thinker@codemud.net>
date Wed, 27 Apr 2011 15:41:47 +0800
parents 17cbb862a8c6
children
line wrap: on
line source

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include "mb_graph_engine.h"
#include "mb_redraw_man.h"
#include "mb_timer.h"
#include "mb_cons_supp.h"
#include "mb_backend.h"
#include "mb_backend_utils.h"
#include "config.h"

#define ERR -1
#define OK 0

#define FALSE 0
#define TRUE 1

#define ASSERT(x)

#define ONLY_MOUSE_MOVE_RAW 1

typedef int keysym;

static mb_timer_factory_t *_timer_factory = &tman_timer_factory;

/*! \ingroup console_kb
 * @{
 */
struct _cons_kb_info {
    int kb_fd;
    
    int keycode_min, keycode_max;
    int ksym_per_code;
    keysym *syms;
    subject_t *kbevents;
    observer_factory_t *observer_factory;
};
typedef struct _cons_kb_info cons_kb_info_t;

/* @} */

struct _cons_supp_runtime {
    MB_DISPLAY display;
    MB_WINDOW win;
    
    mbe_surface_t *surface;
    mbe_t *cr;
    redraw_man_t *rdman;
    mb_img_ldr_t *img_ldr;
    int w, h;

    cons_kb_info_t kbinfo;
    mb_IO_man_t *io_man;
    mb_timer_man_t *timer_man;

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

    /* For handle connection */
    int io_hdl;

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

static void _cons_supp_handle_cons_event(cons_supp_runtime_t *rt);

/*! \defgroup cons_supp_io IO manager for console.
 * @{
 */
#define MAX_MONITORS 200

typedef struct {
    int type;
    int fd;
    mb_IO_cb_t cb;
    void *data;
}  monitor_t;

struct _cons_supp_IO_man {
    mb_IO_man_t io_man;
    monitor_t monitors[MAX_MONITORS];
    int n_monitor;
};

static int _cons_supp_io_man_reg(struct _mb_IO_man *io_man,
				 int fd, MB_IO_TYPE type,
				 mb_IO_cb_t cb, void *data);
static void _cons_supp_io_man_unreg(struct _mb_IO_man *io_man, int io_hdl);
static mb_IO_man_t *_cons_supp_io_man_new(void);
static void _cons_supp_io_man_free(mb_IO_man_t *io_man);

static mb_IO_factory_t _cons_supp_default_io_factory = {
    _cons_supp_io_man_new,
    _cons_supp_io_man_free
};
static mb_IO_factory_t *_io_factory = &_cons_supp_default_io_factory;

static struct _cons_supp_IO_man _default_io_man = {
    {_cons_supp_io_man_reg, _cons_supp_io_man_unreg},
    {},			/* monitors */
    0			/* n_monitor */
};

static mb_IO_man_t *
_cons_supp_io_man_new(void) {
    return (mb_IO_man_t *)&_default_io_man;
}

static void
_cons_supp_io_man_free(mb_IO_man_t *io_man) {
}

static int
_cons_supp_io_man_reg(struct _mb_IO_man *io_man,
		      int fd, MB_IO_TYPE type, mb_IO_cb_t cb, void *data) {
    struct _cons_supp_IO_man *cmb_io_man = (struct _cons_supp_IO_man *)io_man;
    int i;

    for(i = 0; i < cmb_io_man->n_monitor; i++) {
        if (cmb_io_man->monitors[i].type == MB_IO_DUMMY)
	    break;
    }
    if (i == MAX_MONITORS)
	return ERR;
    
    cmb_io_man->monitors[i].type = type;
    cmb_io_man->monitors[i].fd = fd;
    cmb_io_man->monitors[i].cb = cb;
    cmb_io_man->monitors[i].data = data;
    i++;
    if(i > cmb_io_man->n_monitor)
	cmb_io_man->n_monitor = i;
    return i - 1;
}

static void
_cons_supp_io_man_unreg(struct _mb_IO_man *io_man, int io_hdl) {
    struct _cons_supp_IO_man *cmb_io_man = (struct _cons_supp_IO_man *)io_man;
    
    ASSERT(io_hdl < cmb_io_man->n_monitor);
    cmb_io_man->monitors[io_hdl].type = MB_IO_DUMMY;
}

/*! \brief Handle connection coming data and timeout of timers.
 *
 */
static void
_cons_supp_event_loop(mb_rt_t *rt) {
    struct _cons_supp_runtime *cmb_rt = (struct _cons_supp_runtime *)rt;
    struct _cons_supp_IO_man *io_man =
	(struct _cons_supp_IO_man *)cmb_rt->io_man;
    mb_timer_man_t *timer_man = (mb_timer_man_t *)cmb_rt->timer_man;
    redraw_man_t *rdman;
    mb_tman_t *tman = tman_timer_man_get_tman(timer_man);
    mb_timeval_t now, tmo;
    struct timeval tv;
    fd_set rfds, wfds;
    int nfds = 0;
    int r, r1,i;

    rdman = mb_runtime_rdman(rt);
    rdman_redraw_all(rdman);
    
    _cons_supp_handle_cons_event(cmb_rt);

    while(1) {
	FD_ZERO(&rfds);
	FD_ZERO(&wfds);
        for(i = 0; i < io_man->n_monitor; i++) {
	    if(io_man->monitors[i].type == MB_IO_R ||
	       io_man->monitors[i].type == MB_IO_RW) {
		FD_SET(io_man->monitors[i].fd, &rfds);
		nfds = MB_MAX(nfds, io_man->monitors[i].fd + 1);
	    }
	    if(io_man->monitors[i].type == MB_IO_W ||
	       io_man->monitors[i].type == MB_IO_RW) {
		FD_SET(io_man->monitors[i].fd, &wfds);
		nfds = MB_MAX(nfds, io_man->monitors[i].fd + 1);
	    }
        }

	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);
	} else {
            for(i = 0; i < io_man->n_monitor; i++) {
	        if(io_man->monitors[i].type == MB_IO_R ||
		   io_man->monitors[i].type == MB_IO_RW) {
		    if(FD_ISSET(io_man->monitors[i].fd, &rfds))
		    	io_man->monitors[i].cb(i, io_man->monitors[i].fd,
					       MB_IO_R,
					       io_man->monitors[i].data);
		}
		if(io_man->monitors[i].type == MB_IO_W ||
		   io_man->monitors[i].type == MB_IO_RW) {
		    if(FD_ISSET(io_man->monitors[i].fd, &wfds))
			io_man->monitors[i].cb(i, io_man->monitors[i].fd,
					       MB_IO_W,
					       io_man->monitors[i].data);
		}
            }
	}
    }
}

/* @} */

/*! \defgroup console_kb Console Keyboard Handling
 *
 * Accept keyboard events from console and delivery it to
 * application through observer pattern.  There is a subject,
 * per X-connection, for that.
 * @{
 */
static int keycode2sym(cons_kb_info_t *kbinfo, unsigned int keycode) {
    /* TODO: implement keycode to key symbol translation */
    return 0;
}

static int cons_kb_init(cons_kb_info_t *kbinfo, MB_DISPLAY display,
			redraw_man_t *rdman) {
    int n_syms;
    observer_factory_t *factory;
    int r;

    /* TODO: set keycode_min, keycode_max and syms */
    if((int)display != -1)
	kbinfo->kb_fd = (int)display;
    else
	kbinfo->kb_fd = STDIN_FILENO;

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

    return OK;
}

static void cons_kb_destroy(cons_kb_info_t *kbinfo) {
    subject_free(kbinfo->kbevents);
}
/* @} */

/*! \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 keyboard event and maintain internal states.
 *
 * It keeps internal state in rt to improve performance.
 */
static void
handle_single_cons_event(cons_supp_runtime_t *rt) {
    /* TODO: handle keyboard and mouse events. */
    printf("handle_single_cons_event() will be implemented later\n");
}

/*! \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(cons_supp_runtime_t *rt) {
}

/*! \brief Dispatch all console events in the queue.
 */
static void _cons_supp_handle_cons_event(cons_supp_runtime_t *cmb_rt) {
    int console_fd = (int)cmb_rt->display;
    struct pollfd pfd = {console_fd, POLLIN, 0};
    int r;

    while((r = poll(&pfd, 1, 0)) > 0) {
	handle_single_cons_event(cmb_rt);
    }
    no_more_event(cmb_rt);
}

static void
_cons_supp_handle_connection(int hdl, int fd, MB_IO_TYPE type, void *data) {
    cons_supp_runtime_t *cmb_rt = (cons_supp_runtime_t *)data;

    _cons_supp_handle_cons_event(cmb_rt);
}

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

    w = cmb_rt->w;
    h = cmb_rt->h;
    
    mbe_init();
    
    cmb_rt->surface =
	mbe_win_surface_create(cmb_rt->display, cmb_rt->win,
			       MB_IFMT_ARGB32, w, h);

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

    cmb_rt->io_man = mb_io_man_new(_io_factory);
    cmb_rt->timer_man = mb_timer_man_new(_timer_factory);

    img_ldr = simple_mb_img_ldr_new("");
    cmb_rt->img_ldr = img_ldr;
    /*! \todo Remove rdman_set_img_ldr() */
    rdman_set_img_ldr(cmb_rt->rdman, img_ldr); /* this is ncessary? */

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

    cons_kb_init(&cmb_rt->kbinfo, cmb_rt->display, cmb_rt->rdman);

    console_fd = (int)cmb_rt->display;
    cmb_rt->io_hdl = mb_io_man_reg(cmb_rt->io_man, console_fd,
				   MB_IO_R,
				   _cons_supp_handle_connection,
				   cmb_rt);

    return OK;
}

/*! \brief Initialize a MadButterfy runtime for console.
 *
 * It setups a runtime environment to run MadButterfly with console.
 * Users should specify width and height of the opening window.
 *
 * \param display_name is actually the path to the console/input device.
 */
static int _cons_supp_init(cons_supp_runtime_t *cmb_rt,
			   const char *display_name,
			   int w, int h) {
    int r;
    int console_fd;

    memset(cmb_rt, 0, sizeof(cons_supp_runtime_t));

    if(display_name == NULL || strlen(display_name) == 0)
	console_fd = STDIN_FILENO;
    else {
	console_fd = open(display_name, O_RDONLY);
	if(console_fd == -1)
	    return ERR;
    }
    
    cmb_rt->display = (MB_DISPLAY)console_fd;
    cmb_rt->win = NULL;
    cmb_rt->w = w;
    cmb_rt->h = h;
    
    r = _cons_supp_init_with_win_internal(cmb_rt);
    
    return r;
}

/*! \brief Initialize a MadButterfly runtime for a window of console.
 *
 * This function is equivalent to _cons_supp_init() with fixed width
 * and height.  Since, there is no window for console.
 *
 * Runtimes initialized with this function should be destroyed with
 * cons_supp_destroy_keep_win().
 *
 * \param display is actually a file descriptor of console (input device).
 */
static int
_cons_supp_init_with_win(cons_supp_runtime_t *cmb_rt,
			 MB_DISPLAY display, MB_WINDOW win) {
    int r;

    memset(cmb_rt, 0, sizeof(cons_supp_runtime_t));

    cmb_rt->display = display;
    cmb_rt->w = 800;
    cmb_rt->h = 600;
    
    r = _cons_supp_init_with_win_internal(cmb_rt);

    return r;
}

static void cons_supp_destroy_keep_win(cons_supp_runtime_t *cmb_rt);

static void cons_supp_destroy(cons_supp_runtime_t *cmb_rt) {
    int console_fd = cmb_rt = (int)cmb_rt->display;
    
    close(console_fd);
    cons_supp_destroy_keep_win(cmb_rt);
}

/*! \brief Destroy a MadButterfly runtime initialized with
 *	_cons_supp_init_with_win().
 *
 * Destroying a runtime with this function prevent the window and
 * display associated with the runtime being closed.
 */
static void
cons_supp_destroy_keep_win(cons_supp_runtime_t *cmb_rt) {
    if(cmb_rt->rdman) {
	redraw_man_destroy(cmb_rt->rdman);
	free(cmb_rt->rdman);
    }

    if(cmb_rt->io_hdl)
	mb_io_man_unreg(cmb_rt->io_man, cmb_rt->io_hdl);

    if(cmb_rt->io_man)
	mb_io_man_free(_io_factory, cmb_rt->io_man);
    if(cmb_rt->timer_man)
	mb_timer_man_free(_timer_factory, cmb_rt->timer_man);

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

    if(cmb_rt->cr)
	mbe_destroy(cmb_rt->cr);

    if(cmb_rt->surface)
	mbe_surface_destroy(cmb_rt->surface);

    cons_kb_destroy(&cmb_rt->kbinfo);
}

static mb_rt_t *
_cons_supp_new(const char *display_name, int w, int h) {
    cons_supp_runtime_t *rt;
    int r;

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

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

    return (mb_rt_t *)rt;
}

/*! \brief Create a new runtime for existed window for console.
 *
 * The object returned by this function must be free with
 * _cons_supp_free_keep_win() to prevent the window from closed.
 */
static mb_rt_t *
_cons_supp_new_with_win(MB_DISPLAY display, MB_WINDOW win) {
    cons_supp_runtime_t *rt;
    int r;

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

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

    return (mb_rt_t *)rt;
}

static void
_cons_supp_free(mb_rt_t *rt) {
    cons_supp_destroy((cons_supp_runtime_t *) rt);
    free(rt);
}

/*! \brief Free runtime created with _cons_supp_new_with_win().
 */
static void
_cons_supp_free_keep_win(mb_rt_t *rt) {
    cons_supp_destroy_keep_win((cons_supp_runtime_t *) rt);
    free(rt);
}

static subject_t *
_cons_supp_kbevents(mb_rt_t *rt) {
    cons_supp_runtime_t *cmb_rt = (cons_supp_runtime_t *) rt;
    return cmb_rt->kbinfo.kbevents;
}

static redraw_man_t *
_cons_supp_rdman(mb_rt_t *rt) {
    cons_supp_runtime_t *cmb_rt = (cons_supp_runtime_t *) rt;
    return cmb_rt->rdman;
}

static mb_timer_man_t *
_cons_supp_timer_man(mb_rt_t *rt) {
    cons_supp_runtime_t *cmb_rt = (cons_supp_runtime_t *) rt;
    return cmb_rt->timer_man;
}

static observer_factory_t *
_cons_supp_observer_factory(mb_rt_t *rt) {
    cons_supp_runtime_t *cmb_rt = (cons_supp_runtime_t *) rt;
    observer_factory_t *factory;

    factory = rdman_get_observer_factory(cmb_rt->rdman);
    return factory;
}

static mb_img_ldr_t *
_cons_supp_img_ldr(mb_rt_t *rt) {
    cons_supp_runtime_t *cmb_rt = (cons_supp_runtime_t *) rt;
    mb_img_ldr_t *img_ldr;

    img_ldr = cmb_rt->img_ldr;

    return img_ldr;
}

static int
_cons_supp_add_event(mb_rt_t *rt, int fd, MB_IO_TYPE type,
		     mb_IO_cb_t cb, void *data)
{
    cons_supp_runtime_t *cmb_rt = (cons_supp_runtime_t *) rt;
    mb_IO_man_t *io_man = cmb_rt->io_man;
    int hdl;

    hdl = mb_io_man_reg(io_man, fd, type, cb, data);
    return hdl;
}

static void
_cons_supp_remove_event(mb_rt_t *rt, int hdl)
{
    cons_supp_runtime_t *cmb_rt = (cons_supp_runtime_t *) rt;
    mb_IO_man_t *io_man = cmb_rt->io_man;

    mb_io_man_unreg(io_man, hdl);
}

static int
_cons_supp_flush(mb_rt_t *rt) {
    cons_supp_runtime_t *cmb_rt = (cons_supp_runtime_t *)rt;
    
    mbe_flush(cmb_rt->cr);
    return OK;
}

static void
_cons_supp_reg_IO_factory(mb_IO_factory_t *io_factory) {
    _io_factory = io_factory;
}

static void
_cons_supp_reg_timer_factory(mb_timer_factory_t *timer_factory) {
    _timer_factory = timer_factory;
}

mb_backend_t mb_dfl_backend = { _cons_supp_new,
				_cons_supp_new_with_win,
				
				_cons_supp_free,
				_cons_supp_free_keep_win,
				_cons_supp_add_event,
				_cons_supp_remove_event,
				_cons_supp_event_loop,
				_cons_supp_flush,
				
				_cons_supp_kbevents,
				_cons_supp_rdman,
				_cons_supp_timer_man,
				_cons_supp_observer_factory,
				_cons_supp_img_ldr,

				_cons_supp_reg_IO_factory,
				_cons_supp_reg_timer_factory,
};

/*
 * Local Variables:
 * indent-tabs-mode: t
 * tab-width: 8
 * c-basic-offset: 4
 * c-file-style:"stroustrup"
 * fill-column:79
 * End:
 */
// vim: sw=4:ts=8:sts=4:textwidth=79