]> git.proxmox.com Git - spiceterm.git/commitdiff
add first version of spiceterm (copied from vncterm)
authorDietmar Maurer <dietmar@proxmox.com>
Tue, 6 Aug 2013 08:58:17 +0000 (10:58 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Tue, 6 Aug 2013 08:58:17 +0000 (10:58 +0200)
Input does not work for now, because spice only delivers scan-codes.

Makefile
spiceterm.c [new file with mode: 0644]
spiceterm.h [new file with mode: 0644]
test_display_base.c
test_display_base.h

index d028f2b4b3e455c8e5437da430dc5e8c45267602..77b93c95d42d0dcb4b0d3f06aee4ea45a7b7ddc4 100644 (file)
--- 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 (file)
index 0000000..7985ebb
--- /dev/null
@@ -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 <dietmar@proxmox.com>
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h> 
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pty.h>  /* for openpty and forkpty */
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <locale.h>
+
+#include "spiceterm.h"
+#include "glyphs.h"
+
+#include <spice.h>
+#include <spice/enums.h>
+#include <spice/macros.h>
+#include <spice/qxl_dev.h>
+
+#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 (file)
index 0000000..31c914d
--- /dev/null
@@ -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
+  }
+};
index c896761398d18d7ce9e9f745308d91678d31d7d8..dfd869f0f7dfbb727f30c5a13b28e4278d502adb 100644 (file)
@@ -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);
 }
 
index fbabff38c3c90832c871056e0bbba3f2b4a0905e..948f5be0892367db83148b30d59967f6e7ddf7e7 100644 (file)
@@ -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);