Mercurial > MadButterfly
diff src/cons_supp.c @ 1116:9163eefa0039
console backend
author | Thinker K.F. Li <thinker@codemud.net> |
---|---|
date | Wed, 08 Dec 2010 23:41:15 +0800 |
parents | |
children | 1de8bb740c46 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cons_supp.c Wed Dec 08 23:41:15 2010 +0800 @@ -0,0 +1,656 @@ +// -*- 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 <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; + + 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, NULL, + 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->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 X. + * + * 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, +};