From 22e5ba0286ccbc72e492f737a8399d48eb0f94c0 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Tue, 6 Aug 2013 10:58:17 +0200 Subject: [PATCH] add first version of spiceterm (copied from vncterm) Input does not work for now, because spice only delivers scan-codes. --- Makefile | 22 +- spiceterm.c | 1695 +++++++++++++++++++++++++++++++++++++++++++ spiceterm.h | 226 ++++++ test_display_base.c | 31 +- test_display_base.h | 13 + 5 files changed, 1979 insertions(+), 8 deletions(-) create mode 100644 spiceterm.c create mode 100644 spiceterm.h diff --git a/Makefile b/Makefile index d028f2b..77b93c9 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,21 @@ -PROGRAMS=test_display_no_ssl -HEADERS=test_display_base.h basic_event_loop.h ring.h glyphs.h -SOURCES=test_display_base.c test_display_no_ssl.c basic_event_loop.c +PROGRAMS=test_display_no_ssl spiceterm + +HEADERS=test_display_base.h basic_event_loop.h ring.h glyphs.h spiceterm.h +SOURCES=test_display_base.c basic_event_loop.c all: ${PROGRAMS} -test_display_no_ssl: ${SOURCES} ${HEADERS} - gcc ${SOURCES} -o $@ $(shell pkg-config --cflags --libs spice-protocol,spice-server) +test_display_no_ssl: ${SOURCES} ${HEADERS} test_display_no_ssl.c + gcc ${SOURCES} test_display_no_ssl.c -o $@ $(shell pkg-config --cflags --libs spice-protocol,spice-server) + +spiceterm: ${SOURCES} ${HEADERS} spiceterm.c + gcc ${SOURCES} spiceterm.c -o $@ -lutil $(shell pkg-config --cflags --libs spice-protocol,spice-server) -.PHONY: test -test: test_display_no_ssl +.PHONY: test1 +test1: test_display_no_ssl ./test_display_no_ssl & remote-viewer spice://localhost:5912 + +.PHONY: test2 +test2: spiceterm + ./spiceterm & remote-viewer spice://localhost:5912 diff --git a/spiceterm.c b/spiceterm.c new file mode 100644 index 0000000..7985ebb --- /dev/null +++ b/spiceterm.c @@ -0,0 +1,1695 @@ +/* + + Copyright (C) 2007-2011 Proxmox Server Solutions GmbH + + Copyright: spiceterm is under GNU GPL, the GNU General Public License. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 dated June, 1991. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + + Author: Dietmar Maurer + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include /* for openpty and forkpty */ +#include +#include +#include +#include +#include +#include + +#include "spiceterm.h" +#include "glyphs.h" + +#include +#include +#include +#include + +#include "test_display_base.h" + +/* define this for debugging */ +//#define DEBUG + +#define TERM "xterm" + +#define TERMIDCODE "[?1;2c" // vt100 ID + +#define CHECK_ARGC(argc,argv,i) if (i >= argc-1) { \ + fprintf (stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \ + print_usage (NULL); \ + exit(1); \ +} + +/* these colours are from linux kernel drivers/char/vt.c */ + +static int idle_timeout = 1; + +unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7, + 8,12,10,14, 9,13,11,15 }; + +/* the default colour table, for VGA+ colour systems */ +int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa, + 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff}; +int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa, + 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff}; +int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa, + 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff}; + +static void +print_usage (const char *msg) +{ + if (msg) { fprintf (stderr, "ERROR: %s\n", msg); } + fprintf (stderr, "USAGE: vncterm [vncopts] [-c command [args]]\n"); +} + +/* Convert UCS2 to UTF8 sequence, trailing zero */ +static int +ucs2_to_utf8 (unicode c, char *out) +{ + if (c < 0x80) { + out[0] = c; // 0******* + out[1] = 0; + return 1; + } else if (c < 0x800) { + out[0] = 0xc0 | (c >> 6); // 110***** 10****** + out[1] = 0x80 | (c & 0x3f); + out[2] = 0; + return 2; + } else { + out[0] = 0xe0 | (c >> 12); // 1110**** 10****** 10****** + out[1] = 0x80 | ((c >> 6) & 0x3f); + out[2] = 0x80 | (c & 0x3f); + out[3] = 0; + return 3; + } + + return 0; +} + + +static void +draw_char_at (vncTerm *vt, int x, int y, unicode ch, TextAttributes attrib) +{ + if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { + return; + } + + + int ec = vt_fontmap[ch]; + + test_draw_update_char(vt->screen, x, y, ec, attrib); +} + +static void +vncterm_update_xy (vncTerm *vt, int x, int y) +{ + if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; } + + int y1 = (vt->y_base + y) % vt->total_height; + int y2 = y1 - vt->y_displ; + if (y2 < 0) { + y2 += vt->total_height; + } + if (y2 < vt->height) { + TextCell *c = &vt->cells[y1 * vt->width + x]; + draw_char_at (vt, x, y2, c->ch, c->attrib); + } +} + +static void +vncterm_clear_xy (vncTerm *vt, int x, int y) +{ + if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; } + + int y1 = (vt->y_base + y) % vt->total_height; + int y2 = y1 - vt->y_displ; + if (y2 < 0) { + y2 += vt->total_height; + } + if (y2 < vt->height) { + TextCell *c = &vt->cells[y1 * vt->width + x]; + c->ch = ' '; + c->attrib = vt->default_attrib; + c->attrib.fgcol = vt->cur_attrib.fgcol; + c->attrib.bgcol = vt->cur_attrib.bgcol; + + draw_char_at (vt, x, y, c->ch, c->attrib); + } +} + +static void +vncterm_show_cursor (vncTerm *vt, int show) +{ + int x = vt->cx; + if (x >= vt->width) { + x = vt->width - 1; + } + + int y1 = (vt->y_base + vt->cy) % vt->total_height; + int y = y1 - vt->y_displ; + if (y < 0) { + y += vt->total_height; + } + + if (y < vt->height) { + + TextCell *c = &vt->cells[y1 * vt->width + x]; + + if (show) { + TextAttributes attrib = vt->default_attrib; + attrib.invers = !(attrib.invers); /* invert fg and bg */ + draw_char_at (vt, x, y, c->ch, attrib); + } else { + draw_char_at (vt, x, y, c->ch, c->attrib); + } + } +} + +static void +vncterm_refresh (vncTerm *vt) +{ + int x, y, y1; + + // rfbFillRect (vt->screen, 0, 0, vt->maxx, vt->maxy, vt->default_attrib.bgcol); + + y1 = vt->y_displ; + for(y = 0; y < vt->height; y++) { + TextCell *c = vt->cells + y1 * vt->width; + for(x = 0; x < vt->width; x++) { + draw_char_at (vt, x, y, c->ch, c->attrib); + c++; + } + if (++y1 == vt->total_height) + y1 = 0; + } + //rfbMarkRectAsModified (vt->screen, 0, 0, vt->maxx, vt->maxy); + + vncterm_show_cursor (vt, 1); +} + +static void +vncterm_scroll_down (vncTerm *vt, int top, int bottom, int lines) +{ + if ((top + lines) >= bottom) { + lines = bottom - top -1; + } + + if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) { + return; + } + + g_error("vncterm_scroll_down not implemented"); + + /* + int h = lines * 16; + int y0 = top*16; + int y1 = y0 + h; + int y2 = bottom*16; + int rowstride = vt->screen->paddedWidthInBytes; + int rows = (bottom - top - lines)*16; + + char *in = vt->screen->frameBuffer+y0*rowstride; + char *out = vt->screen->frameBuffer+y1*rowstride; + memmove(out,in, rowstride*rows); + + memset(vt->screen->frameBuffer+y0*rowstride, 0, h*rowstride); + rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2); + + int i; + for(i = bottom - top - lines - 1; i >= 0; i--) { + int src = ((vt->y_base + top + i) % vt->total_height)*vt->width; + int dst = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width; + + memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell)); + } + + for (i = 0; i < lines; i++) { + int j; + TextCell *c = vt->cells + ((vt->y_base + top + i) % vt->total_height)*vt->width; + for(j = 0; j < vt->width; j++) { + c->attrib = vt->default_attrib; + c->ch = ' '; + c++; + } + } + */ +} + +static void +vncterm_scroll_up (vncTerm *vt, int top, int bottom, int lines, int moveattr) +{ + if ((top + lines) >= bottom) { + lines = bottom - top - 1; + } + + if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) { + return; + } + + g_error("vncterm_scroll_down not implemented"); + + /* + int h = lines * 16; + int y0 = top*16; + int y1 = (top + lines)*16; + int y2 = bottom*16; + int rowstride = vt->screen->paddedWidthInBytes; + int rows = (bottom - top - lines)*16; + + char *in = vt->screen->frameBuffer+y1*rowstride; + char *out = vt->screen->frameBuffer+y0*rowstride; + memmove(out,in, rowstride*rows); + + memset(vt->screen->frameBuffer+(y2-h)*rowstride, 0, h*rowstride); + + rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2); + + if (!moveattr) return; + + // move attributes + + int i; + for(i = 0; i < (bottom - top - lines); i++) { + int dst = ((vt->y_base + top + i) % vt->total_height)*vt->width; + int src = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width; + + memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell)); + } + + for (i = 1; i <= lines; i++) { + int j; + TextCell *c = vt->cells + ((vt->y_base + bottom - i) % vt->total_height)*vt->width; + for(j = 0; j < vt->width; j++) { + c->attrib = vt->default_attrib; + c->ch = ' '; + c++; + } + } + */ +} + +static void +vncterm_virtual_scroll (vncTerm *vt, int lines) +{ + if (vt->altbuf || lines == 0) return; + + if (lines < 0) { + lines = -lines; + int i = vt->scroll_height; + if (i > vt->total_height - vt->height) + i = vt->total_height - vt->height; + int y1 = vt->y_base - i; + if (y1 < 0) + y1 += vt->total_height; + for(i = 0; i < lines; i++) { + if (vt->y_displ == y1) break; + if (--vt->y_displ < 0) { + vt->y_displ = vt->total_height - 1; + } + } + } else { + int i; + for(i = 0; i < lines; i++) { + if (vt->y_displ == vt->y_base) break; + if (++vt->y_displ == vt->total_height) { + vt->y_displ = 0; + } + } + + } + + vncterm_refresh (vt); +} +static void +vncterm_respond_esc (vncTerm *vt, const char *esc) +{ + int len = strlen (esc); + int i; + + if (vt->ibuf_count < (IBUFSIZE - 1 - len)) { + vt->ibuf[vt->ibuf_count++] = 27; + for (i = 0; i < len; i++) { + vt->ibuf[vt->ibuf_count++] = esc[i]; + } + } +} + +static void +vncterm_put_lf (vncTerm *vt) +{ + if (vt->cy + 1 == vt->region_bottom) { + + if (vt->altbuf || vt->region_top != 0 || vt->region_bottom != vt->height) { + vncterm_scroll_up (vt, vt->region_top, vt->region_bottom, 1, 1); + return; + } + + if (vt->y_displ == vt->y_base) { + vncterm_scroll_up (vt, vt->region_top, vt->region_bottom, 1, 0); + } + + if (vt->y_displ == vt->y_base) { + if (++vt->y_displ == vt->total_height) { + vt->y_displ = 0; + } + } + + if (++vt->y_base == vt->total_height) { + vt->y_base = 0; + } + + if (vt->scroll_height < vt->total_height) { + vt->scroll_height++; + } + + int y1 = (vt->y_base + vt->height - 1) % vt->total_height; + TextCell *c = &vt->cells[y1 * vt->width]; + int x; + for (x = 0; x < vt->width; x++) { + c->ch = ' '; + c->attrib = vt->default_attrib; + c++; + } + + // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ); + + } else if (vt->cy < vt->height - 1) { + vt->cy += 1; + } +} + + +static void +vncterm_csi_m (vncTerm *vt) +{ + int i; + + for (i = 0; i < vt->esc_count; i++) { + switch (vt->esc_buf[i]) { + case 0: /* reset all console attributes to default */ + vt->cur_attrib = vt->default_attrib; + break; + case 1: + vt->cur_attrib.bold = 1; + break; + case 4: + vt->cur_attrib.uline = 1; + break; + case 5: + vt->cur_attrib.blink = 1; + break; + case 7: + vt->cur_attrib.invers = 1; + break; + case 8: + vt->cur_attrib.unvisible = 1; + break; + case 10: + vt->cur_enc = LAT1_MAP; + // fixme: dispaly controls = 0 ? + // fixme: toggle meta = 0 ? + break; + case 11: + vt->cur_enc = IBMPC_MAP; + // fixme: dispaly controls = 1 ? + // fixme: toggle meta = 0 ? + break; + case 12: + vt->cur_enc = IBMPC_MAP; + // fixme: dispaly controls = 1 ? + // fixme: toggle meta = 1 ? + break; + case 22: + vt->cur_attrib.bold = 0; + break; + case 24: + vt->cur_attrib.uline = 0; + break; + case 25: + vt->cur_attrib.blink = 0; + break; + case 27: + vt->cur_attrib.invers = 0; + break; + case 28: + vt->cur_attrib.unvisible = 0; + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + /* set foreground color */ + vt->cur_attrib.fgcol = color_table [vt->esc_buf[i] - 30]; + break; + case 38: + /* reset color to default, enable underline */ + vt->cur_attrib.fgcol = vt->default_attrib.fgcol; + vt->cur_attrib.uline = 1; + break; + case 39: + /* reset color to default, disable underline */ + vt->cur_attrib.fgcol = vt->default_attrib.fgcol; + vt->cur_attrib.uline = 0; + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + /* set background color */ + vt->cur_attrib.bgcol = color_table [vt->esc_buf[i] - 40]; + break; + case 49: + /* reset background color */ + vt->cur_attrib.bgcol = vt->default_attrib.bgcol; + break; + default: + fprintf (stderr, "unhandled ESC[%d m code\n",vt->esc_buf[i]); + //fixme: implement + } + } +} + +static void +vncterm_save_cursor (vncTerm *vt) +{ + vt->cx_saved = vt->cx; + vt->cy_saved = vt->cy; + vt->cur_attrib_saved = vt->cur_attrib; + vt->charset_saved = vt->charset; + vt->g0enc_saved = vt->g0enc; + vt->g1enc_saved = vt->g1enc; + vt->cur_enc_saved = vt->cur_enc; +} + +static void +vncterm_restore_cursor (vncTerm *vt) +{ + vt->cx = vt->cx_saved; + vt->cy = vt->cy_saved; + vt->cur_attrib = vt->cur_attrib_saved; + vt->charset = vt->charset_saved; + vt->g0enc = vt->g0enc_saved; + vt->g1enc = vt->g1enc_saved; + vt->cur_enc = vt->cur_enc_saved; +} + +static void +vncterm_set_alternate_buffer (vncTerm *vt, int on_off) +{ + int x, y; + + vt->y_displ = vt->y_base; + + if (on_off) { + + if (vt->altbuf) return; + + vt->altbuf = 1; + + /* alternate buffer & cursor */ + + vncterm_save_cursor (vt); + /* save screen to altcels */ + for (y = 0; y < vt->height; y++) { + int y1 = (vt->y_base + y) % vt->total_height; + for (x = 0; x < vt->width; x++) { + vt->altcells[y*vt->width + x] = vt->cells[y1*vt->width + x]; + } + } + + /* clear screen */ + for (y = 0; y <= vt->height; y++) { + for (x = 0; x < vt->width; x++) { + vncterm_clear_xy (vt, x, y); + } + } + + } else { + + if (vt->altbuf == 0) return; + + vt->altbuf = 0; + + /* restore saved data */ + for (y = 0; y < vt->height; y++) { + int y1 = (vt->y_base + y) % vt->total_height; + for (x = 0; x < vt->width; x++) { + vt->cells[y1*vt->width + x] = vt->altcells[y*vt->width + x]; + } + } + + vncterm_restore_cursor (vt); + } + + vncterm_refresh (vt); +} + +static void +vncterm_set_mode (vncTerm *vt, int on_off) +{ + int i; + + for (i = 0; i <= vt->esc_count; i++) { + if (vt->esc_ques) { /* DEC private modes set/reset */ + switch(vt->esc_buf[i]) { + case 10: /* X11 mouse reporting on/off */ + case 1000: + vt->report_mouse = on_off; + break; + case 1049: /* start/end special app mode (smcup/rmcup) */ + vncterm_set_alternate_buffer (vt, on_off); + break; + case 25: /* Cursor on/off */ + case 9: /* X10 mouse reporting on/off */ + case 6: /* Origin relative/absolute */ + case 1: /* Cursor keys in appl mode*/ + case 5: /* Inverted screen on/off */ + case 7: /* Autowrap on/off */ + case 8: /* Autorepeat on/off */ + break; + } + } else { /* ANSI modes set/reset */ + /* fixme: implement me */ + } + } +} + +static void +vncterm_gotoxy (vncTerm *vt, int x, int y) +{ + /* verify all boundaries */ + + if (x < 0) { + x = 0; + } + + if (x >= vt->width) { + x = vt->width - 1; + } + + vt->cx = x; + + if (y < 0) { + y = 0; + } + + if (y >= vt->height) { + y = vt->height - 1; + } + + vt->cy = y; +} + +enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, + EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd, + ESpalette, ESidquery, ESosc1, ESosc2}; + +static void +vncterm_putchar (vncTerm *vt, unicode ch) +{ + int x, y, i, c; + +#ifdef DEBUG + if (!vt->tty_state) + fprintf (stderr, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d\n", vt->tty_state, ch, ch, vt->cur_enc, vt->cx, vt->cy); +#endif + + switch(vt->tty_state) { + case ESesc: + vt->tty_state = ESnormal; + switch (ch) { + case '[': + vt->tty_state = ESsquare; + break; + case ']': + vt->tty_state = ESnonstd; + break; + case '%': + vt->tty_state = ESpercent; + break; + case '7': + vncterm_save_cursor (vt); + break; + case '8': + vncterm_restore_cursor (vt); + break; + case '(': + vt->tty_state = ESsetG0; // SET G0 + break; + case ')': + vt->tty_state = ESsetG1; // SET G1 + break; + case 'M': + /* cursor up (ri) */ + if (vt->cy == vt->region_top) + vncterm_scroll_down (vt, vt->region_top, vt->region_bottom, 1); + else if (vt->cy > 0) { + vt->cy--; + } + break; + case '>': + /* numeric keypad - ignored */ + break; + case '=': + /* appl. keypad - ignored */ + break; + default: +#ifdef DEBUG + fprintf(stderr, "got unhandled ESC%c %d\n", ch, ch); +#endif + break; + } + break; + case ESnonstd: /* Operating System Controls */ + vt->tty_state = ESnormal; + + switch (ch) { + case 'P': /* palette escape sequence */ + for(i = 0; i < MAX_ESC_PARAMS; i++) { + vt->esc_buf[i] = 0; + } + + vt->esc_count = 0; + vt->tty_state = ESpalette; + break; + case 'R': /* reset palette */ + // fixme: reset_palette(vc); + break; + case '0': + case '1': + case '2': + case '4': + vt->osc_cmd = ch; + vt->osc_textbuf[0] = 0; + vt->tty_state = ESosc1; + break; + default: +#ifdef DEBUG + fprintf (stderr, "unhandled OSC %c\n", ch); +#endif + vt->tty_state = ESnormal; + break; + } + break; + case ESosc1: + vt->tty_state = ESnormal; + if (ch == ';') { + vt->tty_state = ESosc2; + } else { +#ifdef DEBUG + fprintf (stderr, "got illegal OSC sequence\n"); +#endif + } + break; + case ESosc2: + if (ch != 0x9c && ch != 7) { + int i = 0; + while (vt->osc_textbuf[i]) i++; + vt->osc_textbuf[i++] = ch; + vt->osc_textbuf[i] = 0; + } else { +#ifdef DEBUG + fprintf (stderr, "OSC:%c:%s\n", vt->osc_cmd, vt->osc_textbuf); +#endif + vt->tty_state = ESnormal; + } + break; + case ESpalette: + if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') + || (ch >= 'a' && ch <= 'f')) { + vt->esc_buf[vt->esc_count++] = (ch > '9' ? (ch & 0xDF) - 'A' + 10 : ch - '0'); + if (vt->esc_count == 7) { + // fixme: this does not work - please test + /* + rfbColourMap *cmap =&vt->screen->colourMap; + + int i = color_table[vt->esc_buf[0]] * 3, j = 1; + cmap->data.bytes[i] = 16 * vt->esc_buf[j++]; + cmap->data.bytes[i++] += vt->esc_buf[j++]; + cmap->data.bytes[i] = 16 * vt->esc_buf[j++]; + cmap->data.bytes[i++] += vt->esc_buf[j++]; + cmap->data.bytes[i] = 16 * vt->esc_buf[j++]; + cmap->data.bytes[i] += vt->esc_buf[j]; + */ + //set_palette(vc); ? + + vt->tty_state = ESnormal; + } + } else + vt->tty_state = ESnormal; + break; + case ESsquare: + for(i = 0; i < MAX_ESC_PARAMS; i++) { + vt->esc_buf[i] = 0; + } + + vt->esc_count = 0; + vt->esc_has_par = 0; + vt->tty_state = ESgetpars; + + if (ch == '>') { + vt->tty_state = ESidquery; + break; + } + + if ((vt->esc_ques = (ch == '?'))) { + break; + } + case ESgetpars: + if (ch >= '0' && ch <= '9') { + vt->esc_has_par = 1; + if (vt->esc_count < MAX_ESC_PARAMS) { + vt->esc_buf[vt->esc_count] = vt->esc_buf[vt->esc_count] * 10 + ch - '0'; + } + break; + } else if (ch == ';') { + vt->esc_count++; + break; + } else { + if (vt->esc_has_par) { + vt->esc_count++; + } + vt->tty_state = ESgotpars; + } + case ESgotpars: + + vt->tty_state = ESnormal; + +#ifdef DEBUG + char *qes = vt->esc_ques ? "?" : ""; + if (vt->esc_count == 0) { + fprintf(stderr, "ESC[%s%c\n", qes, ch); + } else if (vt->esc_count == 1) { + fprintf(stderr, "ESC[%s%d%c\n", qes, vt->esc_buf[0], ch); + } else { + int i; + fprintf(stderr, "ESC[%s%d", qes, vt->esc_buf[0]); + for (i = 1; i < vt->esc_count; i++) { + fprintf(stderr, ";%d", vt->esc_buf[i]); + } + fprintf (stderr, "%c\n", ch); + } +#endif + + switch (ch) { + case 'h': + vncterm_set_mode (vt, 1); + break; + case 'l': + vncterm_set_mode (vt, 0); + break; + case 'm': + if (!vt->esc_count) { + vt->esc_count++; // default parameter 0 + } + vncterm_csi_m (vt); + break; + case 'n': + /* report cursor position */ + /* TODO: send ESC[row;colR */ + break; + case 'A': + /* move cursor up */ + if (vt->esc_buf[0] == 0) { + vt->esc_buf[0] = 1; + } + vt->cy -= vt->esc_buf[0]; + if (vt->cy < 0) { + vt->cy = 0; + } + break; + case 'B': + case 'e': + /* move cursor down */ + if (vt->esc_buf[0] == 0) { + vt->esc_buf[0] = 1; + } + vt->cy += vt->esc_buf[0]; + if (vt->cy >= vt->height) { + vt->cy = vt->height - 1; + } + break; + case 'C': + case 'a': + /* move cursor right */ + if (vt->esc_buf[0] == 0) { + vt->esc_buf[0] = 1; + } + vt->cx += vt->esc_buf[0]; + if (vt->cx >= vt->width) { + vt->cx = vt->width - 1; + } + break; + case 'D': + /* move cursor left */ + if (vt->esc_buf[0] == 0) { + vt->esc_buf[0] = 1; + } + vt->cx -= vt->esc_buf[0]; + if (vt->cx < 0) { + vt->cx = 0; + } + break; + case 'G': + case '`': + /* move cursor to column */ + vncterm_gotoxy (vt, vt->esc_buf[0] - 1, vt->cy); + break; + case 'd': + /* move cursor to row */ + vncterm_gotoxy (vt, vt->cx , vt->esc_buf[0] - 1); + break; + case 'f': + case 'H': + /* move cursor to row, column */ + vncterm_gotoxy (vt, vt->esc_buf[1] - 1, vt->esc_buf[0] - 1); + break; + case 'J': + switch (vt->esc_buf[0]) { + case 0: + /* clear to end of screen */ + for (y = vt->cy; y < vt->height; y++) { + for (x = 0; x < vt->width; x++) { + if (y == vt->cy && x < vt->cx) { + continue; + } + vncterm_clear_xy (vt, x, y); + } + } + break; + case 1: + /* clear from beginning of screen */ + for (y = 0; y <= vt->cy; y++) { + for (x = 0; x < vt->width; x++) { + if (y == vt->cy && x > vt->cx) { + break; + } + vncterm_clear_xy (vt, x, y); + } + } + break; + case 2: + /* clear entire screen */ + for (y = 0; y <= vt->height; y++) { + for (x = 0; x < vt->width; x++) { + vncterm_clear_xy (vt, x, y); + } + } + break; + } + break; + case 'K': + switch (vt->esc_buf[0]) { + case 0: + /* clear to eol */ + for(x = vt->cx; x < vt->width; x++) { + vncterm_clear_xy (vt, x, vt->cy); + } + break; + case 1: + /* clear from beginning of line */ + for (x = 0; x <= vt->cx; x++) { + vncterm_clear_xy (vt, x, vt->cy); + } + break; + case 2: + /* clear entire line */ + for(x = 0; x < vt->width; x++) { + vncterm_clear_xy (vt, x, vt->cy); + } + break; + } + break; + case 'L': + /* insert line */ + c = vt->esc_buf[0]; + + if (c > vt->height - vt->cy) + c = vt->height - vt->cy; + else if (!c) + c = 1; + + vncterm_scroll_down (vt, vt->cy, vt->region_bottom, c); + break; + case 'M': + /* delete line */ + c = vt->esc_buf[0]; + + if (c > vt->height - vt->cy) + c = vt->height - vt->cy; + else if (!c) + c = 1; + + vncterm_scroll_up (vt, vt->cy, vt->region_bottom, c, 1); + break; + case 'T': + /* scroll down */ + c = vt->esc_buf[0]; + if (!c) c = 1; + vncterm_scroll_down (vt, vt->region_top, vt->region_bottom, c); + break; + case 'S': + /* scroll up */ + c = vt->esc_buf[0]; + if (!c) c = 1; + vncterm_scroll_up (vt, vt->region_top, vt->region_bottom, c, 1); + break; + case 'P': + /* delete c character */ + c = vt->esc_buf[0]; + + if (c > vt->width - vt->cx) + c = vt->width - vt->cx; + else if (!c) + c = 1; + + for (x = vt->cx; x < vt->width - c; x++) { + int y1 = (vt->y_base + vt->cy) % vt->total_height; + TextCell *dst = &vt->cells[y1 * vt->width + x]; + TextCell *src = dst + c; + *dst = *src; + vncterm_update_xy (vt, x + c, vt->cy); + src->ch = ' '; + src->attrib = vt->default_attrib; + vncterm_update_xy (vt, x, vt->cy); + } + break; + case 's': + /* save cursor position */ + vncterm_save_cursor (vt); + break; + case 'u': + /* restore cursor position */ + vncterm_restore_cursor (vt); + break; + case 'X': + /* erase c characters */ + c = vt->esc_buf[0]; + if (!c) c = 1; + + if (c > (vt->width - vt->cx)) c = vt->width - vt->cx; + + for(i = 0; i < c; i++) { + vncterm_clear_xy (vt, vt->cx + i, vt->cy); + } + break; + case '@': + /* insert c character */ + c = vt->esc_buf[0]; + if (c > (vt->width - vt->cx)) { + c = vt->width - vt->cx; + } + if (!c) c = 1; + + for (x = vt->width - c; x >= vt->cx; x--) { + int y1 = (vt->y_base + vt->cy) % vt->total_height; + TextCell *src = &vt->cells[y1 * vt->width + x]; + TextCell *dst = src + c; + *dst = *src; + vncterm_update_xy (vt, x + c, vt->cy); + src->ch = ' '; + src->attrib = vt->cur_attrib; + vncterm_update_xy (vt, x, vt->cy); + } + + break; + case 'r': + /* set region */ + if (!vt->esc_buf[0]) + vt->esc_buf[0]++; + if (!vt->esc_buf[1]) + vt->esc_buf[1] = vt->height; + /* Minimum allowed region is 2 lines */ + if (vt->esc_buf[0] < vt->esc_buf[1] && + vt->esc_buf[1] <= vt->height) { + vt->region_top = vt->esc_buf[0] - 1; + vt->region_bottom = vt->esc_buf[1]; + vt->cx = 0; + vt->cy = vt->region_top; +#ifdef DEBUG + fprintf (stderr, "set region %d %d\n", vt->region_top, vt->region_bottom); +#endif + } + + break; + default: +#ifdef DEBUG + if (vt->esc_count == 0) { + fprintf(stderr, "unhandled escape ESC[%s%c\n", qes, ch); + } else if (vt->esc_count == 1) { + fprintf(stderr, "unhandled escape ESC[%s%d%c\n", qes, vt->esc_buf[0], ch); + } else { + int i; + fprintf(stderr, "unhandled escape ESC[%s%d", qes, vt->esc_buf[0]); + for (i = 1; i < vt->esc_count; i++) { + fprintf(stderr, ";%d", vt->esc_buf[i]); + } + fprintf (stderr, "%c\n", ch); + } +#endif + break; + } + vt->esc_ques = 0; + break; + case ESsetG0: // Set G0 + vt->tty_state = ESnormal; + + if (ch == '0') + vt->g0enc = GRAF_MAP; + else if (ch == 'B') + vt->g0enc = LAT1_MAP; + else if (ch == 'U') + vt->g0enc = IBMPC_MAP; + else if (ch == 'K') + vt->g0enc = USER_MAP; + + if (vt->charset == 0) + vt->cur_enc = vt->g0enc; + + break; + case ESsetG1: // Set G1 + vt->tty_state = ESnormal; + + if (ch == '0') + vt->g1enc = GRAF_MAP; + else if (ch == 'B') + vt->g1enc = LAT1_MAP; + else if (ch == 'U') + vt->g1enc = IBMPC_MAP; + else if (ch == 'K') + vt->g1enc = USER_MAP; + + if (vt->charset == 1) + vt->cur_enc = vt->g1enc; + + break; + case ESidquery: // vt100 query id + vt->tty_state = ESnormal; + + if (ch == 'c') { +#ifdef DEBUG + fprintf (stderr, "ESC[>c Query term ID\n"); +#endif + vncterm_respond_esc (vt, TERMIDCODE); + } + break; + case ESpercent: + vt->tty_state = ESnormal; + switch (ch) { + case '@': /* defined in ISO 2022 */ + vt->utf8 = 0; + break; + case 'G': /* prelim official escape code */ + case '8': /* retained for compatibility */ + vt->utf8 = 1; + break; + } + break; + default: // ESnormal + vt->tty_state = ESnormal; + + switch(ch) { + case 0: + break; + case 7: /* alert aka. bell */ + // fixme: + //rfbSendBell(vt->screen); + break; + case 8: /* backspace */ + if (vt->cx > 0) + vt->cx--; + break; + case 9: /* tabspace */ + if (vt->cx + (8 - (vt->cx % 8)) > vt->width) { + vt->cx = 0; + vncterm_put_lf (vt); + } else { + vt->cx = vt->cx + (8 - (vt->cx % 8)); + } + break; + case 10: /* LF,*/ + case 11: /* VT */ + case 12: /* FF */ + vncterm_put_lf (vt); + break; + case 13: /* carriage return */ + vt->cx = 0; + break; + case 14: + /* SI (shift in), select character set 1 */ + vt->charset = 1; + vt->cur_enc = vt->g1enc; + /* fixme: display controls = 1 */ + break; + case 15: + /* SO (shift out), select character set 0 */ + vt->charset = 0; + vt->cur_enc = vt->g0enc; + /* fixme: display controls = 0 */ + break; + case 27: /* esc */ + vt->tty_state = ESesc; + break; + case 127: /* delete */ + /* ignore */ + break; + case 128+27: /* csi */ + vt->tty_state = ESsquare; + break; + default: + if (vt->cx >= vt->width) { + /* line wrap */ + vt->cx = 0; + vncterm_put_lf (vt); + } + + int y1 = (vt->y_base + vt->cy) % vt->total_height; + TextCell *c = &vt->cells[y1*vt->width + vt->cx]; + c->attrib = vt->cur_attrib; + c->ch = ch; + vncterm_update_xy (vt, vt->cx, vt->cy); + vt->cx++; + break; + } + break; + } +} + +static int +vncterm_puts (vncTerm *vt, const char *buf, int len) +{ + unicode tc; + + vncterm_show_cursor (vt, 0); + + while (len) { + unsigned char c = *buf; + len--; + buf++; + + if (vt->tty_state != ESnormal) { + // never translate escape sequence + tc = c; + } else if (vt->utf8 && !vt->cur_enc) { + + if(c & 0x80) { // utf8 multi-byte sequence + + if (vt->utf_count > 0 && (c & 0xc0) == 0x80) { + // inside UTF8 sequence + vt->utf_char = (vt->utf_char << 6) | (c & 0x3f); + vt->utf_count--; + if (vt->utf_count == 0) { + tc = vt->utf_char; + } else { + continue; + } + } else { + // first char of a UTF8 sequence + if ((c & 0xe0) == 0xc0) { + vt->utf_count = 1; + vt->utf_char = (c & 0x1f); + } else if ((c & 0xf0) == 0xe0) { + vt->utf_count = 2; + vt->utf_char = (c & 0x0f); + } else if ((c & 0xf8) == 0xf0) { + vt->utf_count = 3; + vt->utf_char = (c & 0x07); + } else if ((c & 0xfc) == 0xf8) { + vt->utf_count = 4; + vt->utf_char = (c & 0x03); + } else if ((c & 0xfe) == 0xfc) { + vt->utf_count = 5; + vt->utf_char = (c & 0x01); + } else + vt->utf_count = 0; + + continue; + } + } else { + // utf8 single byte + tc = c; + vt->utf_count = 0; + } + + } else { + // never translate controls + if (c >= 32 && c != 127 && c != (128+27)) { + tc = translations[vt->cur_enc][c & 0x0ff]; + } else { + tc = c; + } + } + + vncterm_putchar (vt, tc); + } + + vncterm_show_cursor (vt, 1); + return len; +} + +/* fixme: +void +vncterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl) +{ + vncTerm *vt =(vncTerm *)cl->screen->screenData; + + // seems str is Latin-1 encoded + if (vt->selection) free (vt->selection); + vt->selection = (unicode *)malloc (len*sizeof (unicode)); + int i; + for (i = 0; i < len; i++) { + vt->selection[i] = str[i] & 0xff; + } + vt->selection_len = len; +} +*/ +static void +mouse_report (vncTerm *vt, int butt, int mrx, int mry) +{ + char buf[8]; + + sprintf (buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx), + (char)('!' + mry)); + + vncterm_respond_esc (vt, buf); +} + +void +vncterm_toggle_marked_cell (vncTerm *vt, int pos) +{ + int x= (pos%vt->width)*8; + int y= (pos/vt->width)*16; + +/* fixme: + + int i,j; + rfbScreenInfoPtr s=vt->screen; + + char *b = s->frameBuffer+y*s->width+x; + + for (j=0; j < 16; j++) { + for(i=0; i < 8; i++) { + b[j*s->width+i] ^= 0x0f; + rfbMarkRectAsModified (s, x, y, x+8, y+16); + } + } +*/ +} + +/* fixme: + +void +vncterm_pointer_event (int buttonMask, int x, int y, rfbClientPtr cl) +{ + + vncTerm *vt =(vncTerm *)cl->screen->screenData; + static int button2_released = 1; + static int last_mask = 0; + static int sel_start_pos = 0; + static int sel_end_pos = 0; + int i; + + int cx = x/8; + int cy = y/16; + + if (cx < 0) cx = 0; + if (cx >= vt->width) cx = vt->width - 1; + if (cy < 0) cy = 0; + if (cy >= vt->height) cy = vt->height - 1; + + if (vt->report_mouse && buttonMask != last_mask) { + last_mask = buttonMask; + if (buttonMask & 1) { + mouse_report (vt, 0, cx, cy); + } + if (buttonMask & 2) { + mouse_report (vt, 1, cx, cy); + } + if (buttonMask & 4) { + mouse_report (vt, 2, cx, cy); + } + if (!buttonMask) { + mouse_report (vt, 3, cx, cy); + } + } + + if (buttonMask & 2) { + if(button2_released && vt->selection) { + int i; + for(i = 0; i < vt->selection_len; i++) { + if (vt->ibuf_count < IBUFSIZE - 6) { // uft8 is max 6 characters wide + if (vt->utf8) { + vt->ibuf_count += ucs2_to_utf8 (vt->selection[i], &vt->ibuf[vt->ibuf_count]); + } else { + vt->ibuf[vt->ibuf_count++] = vt->selection[i]; + } + } + } + if (vt->y_displ != vt->y_base) { + vt->y_displ = vt->y_base; + vncterm_refresh (vt); + } + } + button2_released = 0; + } else { + button2_released = 1; + } + + if (buttonMask & 1) { + int pos = cy*vt->width + cx; + + // code borrowed from libvncserver (VNConsole.c) + + if (!vt->mark_active) { + + vt->mark_active = 1; + sel_start_pos = sel_end_pos = pos; + vncterm_toggle_marked_cell (vt, pos); + + } else { + + if (pos != sel_end_pos) { + + if (pos > sel_end_pos) { + cx = sel_end_pos; cy=pos; + } else { + cx=pos; cy=sel_end_pos; + } + + if (cx < sel_start_pos) { + if (cy < sel_start_pos) cy--; + } else { + cx++; + } + + while (cx <= cy) { + vncterm_toggle_marked_cell (vt, cx); + cx++; + } + + sel_end_pos = pos; + } + } + + } else if (vt->mark_active) { + vt->mark_active = 0; + + if (sel_start_pos > sel_end_pos) { + int tmp = sel_start_pos - 1; + sel_start_pos = sel_end_pos; + sel_end_pos = tmp; + } + + int len = sel_end_pos - sel_start_pos + 1; + + if (vt->selection) free (vt->selection); + vt->selection = (unicode *)malloc (len*sizeof (unicode)); + vt->selection_len = len; + char *sel_latin1 = (char *)malloc (len + 1); + + for (i = 0; i < len; i++) { + int pos = sel_start_pos + i; + int x = pos % vt->width; + int y1 = ((pos / vt->width) + vt->y_displ) % vt->total_height; + TextCell *c = &vt->cells[y1*vt->width + x]; + vt->selection[i] = c->ch; + sel_latin1[i] = (char)c->ch; + c++; + } + sel_latin1[len] = 0; + rfbGotXCutText (vt->screen, sel_latin1, len); + free (sel_latin1); + + while (sel_start_pos <= sel_end_pos) { + vncterm_toggle_marked_cell (vt, sel_start_pos++); + } + + } + + rfbDefaultPtrAddEvent (buttonMask, x, y, cl); + +} +*/ + +static int client_count = 0; +static int client_connected = 0; +static int last_client = 1; +static time_t last_time = 0; + +static void my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag) +{ + vncTerm *vt = SPICE_CONTAINEROF(sin, vncTerm, keyboard_sin); + + printf("MYKEYCODE %x\n", frag); + + if (vt->ibuf_count < (IBUFSIZE - 32)) { + + char keySym = 'A'; // fixme; + vt->ibuf[vt->ibuf_count++] = keySym; + + vt->screen->core->watch_update_mask(vt->screen->mwatch, SPICE_WATCH_EVENT_READ|SPICE_WATCH_EVENT_WRITE); + } +} + +static uint8_t my_kbd_get_leds(SpiceKbdInstance *sin) +{ + return 0; +} + +static SpiceKbdInterface my_keyboard_sif = { + .base.type = SPICE_INTERFACE_KEYBOARD , + .base.description = "spiceterm keyboard device", + .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR, + .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR, + .push_scan_freg = my_kbd_push_key, + .get_leds = my_kbd_get_leds, +}; + +vncTerm * +create_vncterm (int argc, char** argv, int maxx, int maxy) +{ + int i; + + Test *test; + + SpiceCoreInterface *core = basic_event_loop_init(); + test = test_new(core); + //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF); + test_add_display_interface(test); + test_add_agent_interface(test->server); + + vncTerm *vt = (vncTerm *)calloc (sizeof(vncTerm), 1); + + vt->keyboard_sin.base.sif = &my_keyboard_sif.base; + spice_server_add_interface(test->server, &vt->keyboard_sin.base); + + /* + rfbColourMap *cmap =&screen->colourMap; + cmap->data.bytes = malloc (16*3); + for(i=0;i<16;i++) { + cmap->data.bytes[i*3 + 0] = default_red[color_table[i]]; + cmap->data.bytes[i*3 + 1] = default_grn[color_table[i]]; + cmap->data.bytes[i*3 + 2] = default_blu[color_table[i]]; + } + cmap->count = 16; + cmap->is16 = FALSE; + screen->serverFormat.trueColour = FALSE; + + screen->kbdAddEvent = vncterm_kbd_event; + + screen->setXCutText = vncterm_set_xcut_text; + + screen->ptrAddEvent = vncterm_pointer_event; + + screen->desktopName = "VNC Command Terminal"; + + screen->newClientHook = new_client; + + */ + + vt->maxx = test->width; + vt->maxy = test->height; + + vt->width = vt->maxx / 8; + vt->height = vt->maxy / 16; + + vt->total_height = vt->height * 20; + vt->scroll_height = 0; + vt->y_base = 0; + vt->y_displ = 0; + + vt->region_top = 0; + vt->region_bottom = vt->height; + + vt->g0enc = LAT1_MAP; + vt->g1enc = GRAF_MAP; + vt->cur_enc = vt->g0enc; + vt->charset = 0; + + /* default text attributes */ + vt->default_attrib.bold = 0; + vt->default_attrib.uline = 0; + vt->default_attrib.blink = 0; + vt->default_attrib.invers = 0; + vt->default_attrib.unvisible = 0; + vt->default_attrib.fgcol = 7; + vt->default_attrib.bgcol = 0; + + vt->cur_attrib = vt->default_attrib; + + vt->cells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->total_height); + + for (i = 0; i < vt->width*vt->total_height; i++) { + vt->cells[i].ch = ' '; + vt->cells[i].attrib = vt->default_attrib; + } + + vt->altcells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->height); + + vt->screen = test; + + return vt; +} + +static void master_watch(int master, int event, void *opaque) +{ + vncTerm *vt = (vncTerm *)opaque; + + printf("CHANNEL EVENT %d\n", event); + + // fixme: if (!vt->mark_active) { + + if (event == SPICE_WATCH_EVENT_READ) { + char buffer[1024]; + int c; + while ((c = read(master, buffer, 1024)) == -1) { + if (errno != EAGAIN) break; + } + if (c == -1) { + g_error("got read error"); // fixme + } + vncterm_puts (vt, buffer, c); + } else { + if (vt->ibuf_count > 0) { + printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count); + write (master, vt->ibuf, vt->ibuf_count); + vt->ibuf_count = 0; // fixme: what if not all data written + } + vt->screen->core->watch_update_mask(vt->screen->mwatch, SPICE_WATCH_EVENT_READ); + } +} + +int +main (int argc, char** argv) +{ + int i; + char **cmdargv = NULL; + char *command = "/bin/bash"; // execute normal shell as default + int pid; + int master; + char ptyname[1024]; + fd_set fs, fs1; + struct timeval tv, tv1; + time_t elapsed, cur_time; + struct winsize dimensions; + + for (i = 1; i < argc; i++) { + if (!strcmp (argv[i], "-c")) { + command = argv[i+1]; + cmdargv = &argv[i+1]; + argc = i; + argv[i] = NULL; + break; + } + } + + vncTerm *vt = create_vncterm (argc, argv, 745, 400); + + setlocale(LC_ALL, ""); // set from environment + + char *ctype = setlocale (LC_CTYPE, NULL); // query LC_CTYPE + + // fixme: ist there a standard way to detect utf8 mode ? + if (strcasestr (ctype, ".utf-8")||strcasestr (ctype, ".utf8")) { + vt->utf8 = 1; + } + + dimensions.ws_col = vt->width; + dimensions.ws_row = vt->height; + + setenv ("TERM", TERM, 1); + + printf("EXEC: %s\n", command); + + pid = forkpty (&master, ptyname, NULL, &dimensions); + if(!pid) { + + // install default signal handlers + signal (SIGQUIT, SIG_DFL); + signal (SIGTERM, SIG_DFL); + signal (SIGINT, SIG_DFL); + + if (cmdargv) { + execvp (command, cmdargv); + } else { + execlp (command, command, NULL); + } + perror ("Error: exec failed\n"); + exit (-1); // should not be reached + } else if (pid == -1) { + perror ("Error: fork failed\n"); + exit (-1); + } + + + vt->screen->mwatch = vt->screen->core->watch_add( + master, SPICE_WATCH_EVENT_READ /* |SPICE_WATCH_EVENT_WRITE */, + master_watch, vt); + + basic_event_loop_mainloop(); + + //rfbProcessEvents (vt->screen, 40000); /* 40 ms */ + + /* + if (vt->ibuf_count > 0) { + printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count); + write (master, vt->ibuf, vt->ibuf_count); + vt->ibuf_count = 0; + last_time = time (NULL); + } + */ + + kill (pid, 9); + int status; + waitpid(pid, &status, 0); + + exit (0); +} diff --git a/spiceterm.h b/spiceterm.h new file mode 100644 index 0000000..31c914d --- /dev/null +++ b/spiceterm.h @@ -0,0 +1,226 @@ +#include "test_display_base.h" + +#define IBUFSIZE 1024 +#define MAX_ESC_PARAMS 16 + +typedef unsigned short unicode; + +typedef struct TextCell { + unicode ch; + TextAttributes attrib; +} TextCell; + +typedef struct vncTerm { + int maxx; + int maxy; + + int width; + int height; + + int total_height; + int scroll_height; + int y_base; + int y_displ; + int altbuf:1; + + unsigned int utf8:1; // utf8 mode + long utf_char; // used by utf8 parser + int utf_count; // used by utf8 parser + + + TextAttributes default_attrib; + + TextCell *cells; + TextCell *altcells; + + Test *screen; + SpiceKbdInstance keyboard_sin; + + // cursor + TextAttributes cur_attrib; + TextAttributes cur_attrib_saved; + int tty_state; // 0 - normal, 1 - ESC, 2 - CSI + int cx; // cursor x position + int cy; // cursor y position + int cx_saved; // saved cursor x position + int cy_saved; // saved cursor y position + int esc_buf[MAX_ESC_PARAMS]; + int esc_count; + int esc_ques; + int esc_has_par; + char osc_textbuf[4096]; + char osc_cmd; + int region_top; + int region_bottom; + + unsigned int charset:1; // G0 or G1 + unsigned int charset_saved:1; // G0 or G1 + unsigned int g0enc:2; + unsigned int g0enc_saved:2; + unsigned int g1enc:2; + unsigned int g1enc_saved:2; + unsigned int cur_enc:2; + unsigned int cur_enc_saved:2; + + // input buffer + char ibuf[IBUFSIZE]; + int ibuf_count; + + unicode *selection; + int selection_len; + + unsigned int mark_active:1; + + unsigned int report_mouse:1; + +} vncTerm; + +/* Unicode translations copied from kernel source consolemap.c */ + +#define LAT1_MAP 0 +#define GRAF_MAP 1 +#define IBMPC_MAP 2 +#define USER_MAP 3 + +static unsigned short translations[][256] = { + /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */ + { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, + 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, + 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, + 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, + 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, + 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, + 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, + 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, + 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, + 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, + 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, + 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, + 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff + }, + /* VT100 graphics mapped to Unicode */ + { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x2192, 0x2190, 0x2191, 0x2193, 0x002f, + 0x2588, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0, + 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, + 0x2591, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, + 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, + 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, + 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, + 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, + 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, + 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, + 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, + 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, + 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, + 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, + 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, + 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, + 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, + 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff + }, + /* IBM Codepage 437 mapped to Unicode */ + { + 0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, + 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c, + 0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8, + 0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, + 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, + 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, + 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, + 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, + 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, + 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, + 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, + 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, + 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, + 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, + 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, + 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, + 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0 + }, + /* User mapping -- default to codes for direct font mapping */ + { + 0xf000, 0xf001, 0xf002, 0xf003, 0xf004, 0xf005, 0xf006, 0xf007, + 0xf008, 0xf009, 0xf00a, 0xf00b, 0xf00c, 0xf00d, 0xf00e, 0xf00f, + 0xf010, 0xf011, 0xf012, 0xf013, 0xf014, 0xf015, 0xf016, 0xf017, + 0xf018, 0xf019, 0xf01a, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, + 0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027, + 0xf028, 0xf029, 0xf02a, 0xf02b, 0xf02c, 0xf02d, 0xf02e, 0xf02f, + 0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, + 0xf038, 0xf039, 0xf03a, 0xf03b, 0xf03c, 0xf03d, 0xf03e, 0xf03f, + 0xf040, 0xf041, 0xf042, 0xf043, 0xf044, 0xf045, 0xf046, 0xf047, + 0xf048, 0xf049, 0xf04a, 0xf04b, 0xf04c, 0xf04d, 0xf04e, 0xf04f, + 0xf050, 0xf051, 0xf052, 0xf053, 0xf054, 0xf055, 0xf056, 0xf057, + 0xf058, 0xf059, 0xf05a, 0xf05b, 0xf05c, 0xf05d, 0xf05e, 0xf05f, + 0xf060, 0xf061, 0xf062, 0xf063, 0xf064, 0xf065, 0xf066, 0xf067, + 0xf068, 0xf069, 0xf06a, 0xf06b, 0xf06c, 0xf06d, 0xf06e, 0xf06f, + 0xf070, 0xf071, 0xf072, 0xf073, 0xf074, 0xf075, 0xf076, 0xf077, + 0xf078, 0xf079, 0xf07a, 0xf07b, 0xf07c, 0xf07d, 0xf07e, 0xf07f, + 0xf080, 0xf081, 0xf082, 0xf083, 0xf084, 0xf085, 0xf086, 0xf087, + 0xf088, 0xf089, 0xf08a, 0xf08b, 0xf08c, 0xf08d, 0xf08e, 0xf08f, + 0xf090, 0xf091, 0xf092, 0xf093, 0xf094, 0xf095, 0xf096, 0xf097, + 0xf098, 0xf099, 0xf09a, 0xf09b, 0xf09c, 0xf09d, 0xf09e, 0xf09f, + 0xf0a0, 0xf0a1, 0xf0a2, 0xf0a3, 0xf0a4, 0xf0a5, 0xf0a6, 0xf0a7, + 0xf0a8, 0xf0a9, 0xf0aa, 0xf0ab, 0xf0ac, 0xf0ad, 0xf0ae, 0xf0af, + 0xf0b0, 0xf0b1, 0xf0b2, 0xf0b3, 0xf0b4, 0xf0b5, 0xf0b6, 0xf0b7, + 0xf0b8, 0xf0b9, 0xf0ba, 0xf0bb, 0xf0bc, 0xf0bd, 0xf0be, 0xf0bf, + 0xf0c0, 0xf0c1, 0xf0c2, 0xf0c3, 0xf0c4, 0xf0c5, 0xf0c6, 0xf0c7, + 0xf0c8, 0xf0c9, 0xf0ca, 0xf0cb, 0xf0cc, 0xf0cd, 0xf0ce, 0xf0cf, + 0xf0d0, 0xf0d1, 0xf0d2, 0xf0d3, 0xf0d4, 0xf0d5, 0xf0d6, 0xf0d7, + 0xf0d8, 0xf0d9, 0xf0da, 0xf0db, 0xf0dc, 0xf0dd, 0xf0de, 0xf0df, + 0xf0e0, 0xf0e1, 0xf0e2, 0xf0e3, 0xf0e4, 0xf0e5, 0xf0e6, 0xf0e7, + 0xf0e8, 0xf0e9, 0xf0ea, 0xf0eb, 0xf0ec, 0xf0ed, 0xf0ee, 0xf0ef, + 0xf0f0, 0xf0f1, 0xf0f2, 0xf0f3, 0xf0f4, 0xf0f5, 0xf0f6, 0xf0f7, + 0xf0f8, 0xf0f9, 0xf0fa, 0xf0fb, 0xf0fc, 0xf0fd, 0xf0fe, 0xf0ff + } +}; diff --git a/test_display_base.c b/test_display_base.c index c896761..dfd869f 100644 --- a/test_display_base.c +++ b/test_display_base.c @@ -184,7 +184,6 @@ static SimpleSpiceUpdate *test_draw_char(Test *test, int x, int y, int c) return test_spice_create_update_from_bitmap(0, bbox, bitmap); } - static void create_primary_surface(Test *test, uint32_t width, uint32_t height) { @@ -565,6 +564,35 @@ static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag) test->qxl_worker->wakeup(test->qxl_worker); } +void test_draw_update_char(Test *test, int x, int y, int c, TextAttributes attrib) +{ + int fg, bg; + + if (attrib.invers) { + bg = attrib.fgcol; + fg = attrib.bgcol; + } else { + bg = attrib.bgcol; + fg = attrib.fgcol; + } + + if (attrib.bold) { + fg += 8; + } + + // unsuported attributes = (attrib.blink || attrib.unvisible) + + //if (attrib.uline) { + //rfbDrawLine (vt->screen, rx, ry + 14, rxe, ry + 14, fg); + //} + + SimpleSpiceUpdate *update; + update = test_draw_char(test, x, y, c); + push_command(&update->ext); + + test->qxl_worker->wakeup(test->qxl_worker); +} + static uint8_t kbd_get_leds(SpiceKbdInstance *sin) { return 0; @@ -581,6 +609,7 @@ static SpiceKbdInterface keyboard_sif = { void test_add_keyboard_interface(Test* test) { + g_error("teste"); spice_server_add_interface(test->server, &test->keyboard_sin.base); } diff --git a/test_display_base.h b/test_display_base.h index fbabff3..948f5be 100644 --- a/test_display_base.h +++ b/test_display_base.h @@ -6,6 +6,16 @@ #include "basic_event_loop.h" +typedef struct TextAttributes { + unsigned int fgcol:4; + unsigned int bgcol:4; + unsigned int bold:1; + unsigned int uline:1; + unsigned int blink:1; + unsigned int invers:1; + unsigned int unvisible:1; +} TextAttributes; + #define COUNT(x) ((sizeof(x)/sizeof(x[0]))) /* @@ -96,6 +106,7 @@ struct Test { int primary_width; SpiceTimer *conn_timeout_timer; + SpiceWatch *mwatch; /* watch master pty */ int cursor_notify; @@ -125,6 +136,8 @@ void test_add_agent_interface(SpiceServer *server); // TODO - Test *test void test_add_keyboard_interface(Test *test); Test* test_new(SpiceCoreInterface* core); +void test_draw_update_char(Test *test, int x, int y, int c, TextAttributes attrib); + uint32_t test_get_width(void); uint32_t test_get_height(void); -- 2.39.2