X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=palacios%2Fsrc%2Fdevices%2Fcurses_cons.c;h=527278933a2bf662170f97a37b4b957ade0336df;hb=198151f1d58834ff7889389007232a3d250f51f1;hp=b30ee021d2c2607b1420939776cb7d405c291568;hpb=e5e029575affad90c19ca038a3b780820535d421;p=palacios.git diff --git a/palacios/src/devices/curses_cons.c b/palacios/src/devices/curses_cons.c index b30ee02..5272789 100644 --- a/palacios/src/devices/curses_cons.c +++ b/palacios/src/devices/curses_cons.c @@ -21,199 +21,269 @@ /* Interface between virtual video card and console */ #include -#include +#include #include #include #include #include #include +#include #include -#define NUM_ROWS 25 -#define NUM_COLS 80 -#define BYTES_PER_COL 2 -#define BYTES_PER_ROW (NUM_COLS * BYTES_PER_COL) +#ifndef DEBUG_CURSES_CONS +#undef PrintDebug +#define PrintDebug(fmt, args...) +#endif -#define SCREEN_SIZE (BYTES_PER_ROW * NUM_ROWS) +#define BYTES_PER_COL 2 -struct cons_state -{ - void *tty; - struct vm_device *frontend_dev; +struct cons_state { + v3_console_t cons; + int rows; + int cols; + uint8_t * framebuf; + struct vm_device * frontend_dev; }; static int screen_update(uint_t x, uint_t y, uint_t length, void *private_data); -static uint_t last_offset; +static int screen_update_all(void * private_data) { + struct vm_device *dev = (struct vm_device *) private_data; + struct cons_state *state = (struct cons_state *)dev->private_data; + uint_t screen_size; + + screen_size = state->cols * state->rows * BYTES_PER_COL; + return screen_update(0, 0, screen_size, private_data); +} static int cursor_update(uint_t x, uint_t y, void *private_data) { - struct vm_device *dev = (struct vm_device *) private_data; - struct cons_state *state = (struct cons_state *) dev->private_data; - uint_t offset = (x * BYTES_PER_COL) + (y * BYTES_PER_ROW); - uint_t last_x, last_y; - - /* unfortunately Palacios sometimes misses some writes, - * but if they are accompanied by a cursor move we may be able to - * detect this - */ - if (offset < last_offset) last_offset = 0; - if (offset > last_offset) { - last_x = (last_offset % BYTES_PER_ROW) / BYTES_PER_COL; - last_y = last_offset / BYTES_PER_ROW; - screen_update(last_x, last_y, offset - last_offset, private_data); - } + struct vm_device *dev = (struct vm_device *) private_data; + struct cons_state *state = (struct cons_state *) dev->private_data; + uint_t offset; + + PrintDebug(VM_NONE, VCORE_NONE, "cursor_update(%d, %d, %p)\n", x, y, private_data); + + /* avoid out-of-range coordinates */ + if (x >= state->cols) x = state->cols - 1; + if (y >= state->rows) y = state->rows - 1; + offset = (x + y * state->cols) * BYTES_PER_COL; + + /* adjust cursor */ + if (v3_console_set_cursor(state->cons, x, y) < 0) { + PrintError(VM_NONE, VCORE_NONE, "set cursor (0x%p, %d, %d) failed\n", state->cons, x, y); + return -1; + } + + /* done with console update */ + if (v3_console_update(state->cons) < 0) { + PrintError(VM_NONE, VCORE_NONE, "console update (0x%p) failed\n", state->cons); + return -1; + } + + return 0; +} - /* adjust cursor */ - if (V3_TtyCursorSet(state->tty, x, y) < 0) { - PrintError("V3_TtyCursorSet(0x%p, %d, %d) failed\n", state->tty, x, y); - return -1; - } +static int screen_update(uint_t x, uint_t y, uint_t length, void * private_data) { + struct vm_device * dev = (struct vm_device *)private_data; + struct cons_state * state = (struct cons_state *)dev->private_data; + uint_t offset = (x + y * state->cols) * BYTES_PER_COL; + int i; + uint_t cur_x = x; + uint_t cur_y = y; + + if (length > (state->rows * state->cols * BYTES_PER_COL)) { + PrintError(VM_NONE, VCORE_NONE, "Screen update larger than curses framebuffer\n"); + return 0; + } - /* done with console update */ - if (V3_TtyUpdate(state->tty) < 0) { - PrintError("V3_TtyUpdate(0x%p) failed\n", state->tty); - return -1; - } + PrintDebug(VM_NONE, VCORE_NONE, "screen_update(%d, %d, %d, %p)\n", x, y, length, private_data); - return 0; + /* grab frame buffer */ + v3_cons_get_fb(state->frontend_dev, state->framebuf, offset, length); + + /* update the screen */ + for (i = 0; i < length; i += 2) { + uint_t col_index = i; + uint8_t col[2]; + + col[0] = state->framebuf[col_index]; // Character + col[1] = state->framebuf[col_index + 1]; // Attribute + + /* update current character */ + if (v3_console_set_char(state->cons, cur_x, cur_y, col[0], col[1]) < 0) { + PrintError(VM_NONE, VCORE_NONE, "set cursor (0x%p, %d, %d, %d, %d) failed\n", + state->cons, cur_x, cur_y, col[1], col[0]); + return -1; + } + + // CAUTION: the order of these statements is critical + // cur_y depends on the previous value of cur_x + cur_y = cur_y + ((cur_x + 1) / state->cols); + cur_x = (cur_x + 1) % state->cols; + } + + /* done with console update */ + if (v3_console_update(state->cons) < 0) { + PrintError(VM_NONE, VCORE_NONE, "console update(0x%p) failed\n", state->cons); + return -1; + } + + return 0; } -static int screen_update(uint_t x, uint_t y, uint_t length, void *private_data) -{ - struct vm_device *dev = (struct vm_device *)private_data; - struct cons_state *state = (struct cons_state *)dev->private_data; - uint_t offset = (x * BYTES_PER_COL) + (y * BYTES_PER_ROW); - uint8_t fb_buf[length]; - int i; - uint_t cur_x = x; - uint_t cur_y = y; - - /* grab frame buffer */ - memset(fb_buf, 0, length); - v3_cons_get_fb(state->frontend_dev, fb_buf, offset, length); - - /* update the screen */ - for (i = 0; i < length; i += 2) - { - uint_t col_index = i; - uint8_t col[2]; - - col[0] = fb_buf[col_index]; // Character - col[1] = fb_buf[col_index + 1]; // Attribute - - /* update current character */ - if (V3_TtyCharacterSet(state->tty, cur_x, cur_y, col[0], col[1]) < 0) { - PrintError("V3_TtyCursorSet(0x%p, %d, %d, %d, %d) failed\n", - state->tty, cur_x, cur_y, col[1], col[0]); - return -1; - } - - // CAUTION: the order of these statements is critical - // cur_y depends on the previous value of cur_x - cur_y = cur_y + ((cur_x + 1) / NUM_COLS); - cur_x = (cur_x + 1) % NUM_COLS; +static int scroll(int rows, void * private_data) { + struct vm_device *dev = (struct vm_device *)private_data; + struct cons_state *state = (struct cons_state *)dev->private_data; + + PrintDebug(VM_NONE, VCORE_NONE, "scroll(%d, %p)\n", rows, private_data); + + if (rows < 0) { + /* simply update the screen */ + return screen_update_all(private_data); + } + + if (rows > 0) { + /* scroll requested number of lines*/ + if (v3_console_scroll(state->cons, rows) < 0) { + PrintError(VM_NONE, VCORE_NONE, "console scroll (0x%p, %u) failed\n", state->cons, rows); + return -1; } /* done with console update */ - if (V3_TtyUpdate(state->tty) < 0) { - PrintError("V3_TtyUpdate(0x%p) failed\n", state->tty); - return -1; + if (v3_console_update(state->cons) < 0) { + PrintError(VM_NONE, VCORE_NONE, "console update (0x%p) failed\n", state->cons); + return -1; } + } + + return 0; +} - /* store offset to catch missing notifications */ - last_offset = offset + length; +static int set_text_resolution(int cols, int rows, void * private_data) { + struct vm_device *dev = (struct vm_device *)private_data; + struct cons_state *state = (struct cons_state *)dev->private_data; - return 0; + PrintDebug(VM_NONE, VCORE_NONE, "set_text_resolution(%d, %d, %p)\n", cols, rows, private_data); + + /* store resolution for internal use */ + V3_ASSERT(VM_NONE, VCORE_NONE, cols >= 1); + V3_ASSERT(VM_NONE, VCORE_NONE, rows >= 1); + state->cols = cols; + state->rows = rows; + + /* set notification regarding resolution change */ + if (v3_console_set_text_resolution(state->cons, cols, rows) < 0) { + PrintError(VM_NONE, VCORE_NONE, "console set_text_resolution (0x%p, %u, %u) failed\n", state->cons, cols, rows); + return -1; + } + + /* update the screen */ + return screen_update_all(private_data); } -static int scroll(int rows, void *private_data) -{ - struct vm_device *dev = (struct vm_device *)private_data; - struct cons_state *state = (struct cons_state *)dev->private_data; +static int cons_free(struct cons_state * state) { + v3_console_close(state->cons); - if (rows < 0) { - /* simply update the screen */ - return screen_update(0, 0, SCREEN_SIZE, private_data); - } + // remove host event - if (rows > 0) { - /* scroll requested number of lines*/ - if (V3_TtyScroll(state->tty, rows) < 0) { - PrintError("V3_TtyScroll(0x%p, %u) failed\n", state->tty, rows); - return -1; - } - - /* done with console update */ - if (V3_TtyUpdate(state->tty) < 0) { - PrintError("V3_TtyUpdate(0x%p) failed\n", state->tty); - return -1; - } - - last_offset = BYTES_PER_ROW * (NUM_ROWS - 1); - } - - return 0; + V3_Free(state); + + return 0; +} + +static int console_event_handler(struct v3_vm_info * vm, + struct v3_console_event * evt, + void * priv_data) { + return screen_update_all(priv_data); } static struct v3_console_ops cons_ops = { .update_screen = screen_update, .update_cursor = cursor_update, .scroll = scroll, + .set_text_resolution = set_text_resolution, }; static struct v3_device_ops dev_ops = { - .free = NULL, - .reset = NULL, - .start = NULL, - .stop = NULL, + .free = (int (*)(void *))cons_free, }; static int cons_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { - struct cons_state * state = NULL; - v3_cfg_tree_t * frontend_cfg = v3_cfg_subtree(cfg, "frontend"); - const char * frontend_tag = v3_cfg_val(frontend_cfg, "tag"); - struct vm_device * frontend = v3_find_dev(vm, frontend_tag); - char * dev_id = v3_cfg_val(cfg, "ID"); - char * ttypath = v3_cfg_val(cfg, "tty"); - - /* read configuration */ - V3_ASSERT(frontend_cfg); - V3_ASSERT(frontend_tag); - V3_ASSERT(frontend); - - - /* allocate state */ - state = (struct cons_state *)V3_Malloc(sizeof(struct cons_state)); - V3_ASSERT(state); - state->frontend_dev = frontend; - V3_ASSERT(ttypath); - - /* open tty for screen display */ - state->tty = V3_TtyOpen(ttypath, TTY_OPEN_MODE_READ | TTY_OPEN_MODE_WRITE); - if (!state->tty) { - PrintError("Could not open tty %s\n", ttypath); - V3_Free(state); - return -1; - } - - /* allocate device */ - struct vm_device *dev = v3_allocate_device(dev_id, &dev_ops, state); - V3_ASSERT(dev); - - /* attach device to virtual machine */ - if (v3_attach_device(vm, dev) == -1) { - PrintError("Could not attach device %s\n", dev_id); - V3_Free(state); - return -1; - } - - /* attach to front-end display adapter */ - v3_console_register_cga(frontend, &cons_ops, dev); - - return 0; + struct cons_state * state = NULL; + v3_cfg_tree_t * frontend_cfg; + const char * frontend_tag; + struct vm_device * frontend; + char * dev_id = v3_cfg_val(cfg, "ID"); + + /* read configuration */ + frontend_cfg = v3_cfg_subtree(cfg, "frontend"); + if (!frontend_cfg) { + PrintError(vm, VCORE_NONE, "No frontend specification for curses console.\n"); + return -1; + } + + frontend_tag = v3_cfg_val(frontend_cfg, "tag"); + if (!frontend_tag) { + PrintError(vm, VCORE_NONE, "No frontend device tag specified for curses console.\n"); + return -1; + } + + frontend = v3_find_dev(vm, frontend_tag); + if (!frontend) { + PrintError(vm, VCORE_NONE, "Could not find frontend device %s for curses console.\n", + frontend_tag); + return -1; + } + + /* allocate state */ + state = (struct cons_state *)V3_Malloc(sizeof(struct cons_state)); + + if (!state) { + PrintError(vm, VCORE_NONE, "Cannot allocate curses state\n"); + V3_Free(state); + return -1; + } + + state->frontend_dev = frontend; + state->cols = 80; + state->rows = 25; + state->framebuf = V3_Malloc(state->cols * state->rows * BYTES_PER_COL); + + if (!state->framebuf) { + PrintError(vm, VCORE_NONE, "Cannot allocate frame buffer\n"); + V3_Free(state); + return -1; + } + + /* open tty for screen display */ + state->cons = v3_console_open(vm, state->cols, state->rows); + + if (!state->cons) { + PrintError(vm, VCORE_NONE, "Could not open console\n"); + V3_Free(state->framebuf); + V3_Free(state); + return -1; + } + + /* allocate device */ + struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, state); + + if (dev == NULL) { + PrintError(vm, VCORE_NONE, "Could not attach device %s\n", dev_id); + V3_Free(state->framebuf); + V3_Free(state); + return -1; + } + + /* attach to front-end display adapter */ + v3_console_register_cga(frontend, &cons_ops, dev); + + v3_hook_host_event(vm, HOST_CONSOLE_EVT, V3_HOST_EVENT_HANDLER(console_event_handler), dev); + + return 0; } device_register("CURSES_CONSOLE", cons_init)