]> git.proxmox.com Git - spiceterm.git/blobdiff - spiceterm.c
test if keysym is zero
[spiceterm.git] / spiceterm.c
index 6b10173b1eeb8636a75a34d15a6d89fa088e1d56..83eb4c8249366cc6ac467ab5b9ff188fbba63020 100644 (file)
@@ -79,6 +79,11 @@ unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
                                8,12,10,14, 9,13,11,15 };
 
 
+static void spiceterm_resize(spiceTerm *vt, uint32_t width, uint32_t height);
+
+static void vdagent_grab_clipboard(spiceTerm *vt, uint8_t selection);
+static void vdagent_request_clipboard(spiceTerm *vt, uint8_t selection);
+
 static void
 print_usage(const char *msg)
 {
@@ -86,34 +91,8 @@ print_usage(const char *msg)
     fprintf(stderr, "USAGE: spiceterm [spiceopts] [-c command [args]]\n");
 }
 
-/* Convert UCS2 to UTF8 sequence, trailing zero */
-/*
-static int
-ucs2_to_utf8 (gunichar2 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 (spiceTerm *vt, int x, int y, gunichar2 ch, TextAttributes attrib)
+draw_char_at(spiceTerm *vt, int x, int y, gunichar2 ch, TextAttributes attrib)
 {
     if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) {
         return;
@@ -123,7 +102,7 @@ draw_char_at (spiceTerm *vt, int x, int y, gunichar2 ch, TextAttributes attrib)
 }
 
 static void
-spiceterm_update_xy (spiceTerm *vt, int x, int y)
+spiceterm_update_xy(spiceTerm *vt, int x, int y)
 {
     if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
 
@@ -134,12 +113,12 @@ spiceterm_update_xy (spiceTerm *vt, int x, int y)
     }
     if (y2 < vt->height) {
         TextCell *c = &vt->cells[y1 * vt->width + x];
-        draw_char_at (vt, x, y2, c->ch, c->attrib);
+        draw_char_at(vt, x, y2, c->ch, c->attrib);
     }
 }
 
 static void
-spiceterm_clear_xy (spiceTerm *vt, int x, int y)
+spiceterm_clear_xy(spiceTerm *vt, int x, int y)
 {
     if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
 
@@ -155,12 +134,30 @@ spiceterm_clear_xy (spiceTerm *vt, int x, int y)
         c->attrib.fgcol = vt->cur_attrib.fgcol;
         c->attrib.bgcol = vt->cur_attrib.bgcol;
 
-        draw_char_at (vt, x, y, c->ch, c->attrib);
+        draw_char_at(vt, x, y, c->ch, c->attrib);
+    }
+}
+
+void
+spiceterm_toggle_marked_cell(spiceTerm *vt, int pos)
+{
+    int x = (pos%vt->width);
+    int y = (pos/vt->width);
+
+    if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
+
+    int y1 = (vt->y_displ + y) % vt->total_height;
+
+    TextCell *c = &vt->cells[y1 * vt->width + x];
+    c->attrib.selected =  c->attrib.selected ? 0 : 1;
+
+    if (y < vt->height) {
+        draw_char_at(vt, x, y, c->ch, c->attrib);
     }
 }
 
 static void
-spiceterm_show_cursor (spiceTerm *vt, int show)
+spiceterm_show_cursor(spiceTerm *vt, int show)
 {
     int x = vt->cx;
     if (x >= vt->width) {
@@ -180,38 +177,78 @@ spiceterm_show_cursor (spiceTerm *vt, int show)
         if (show) {
             TextAttributes attrib = vt->default_attrib;
             attrib.invers = !(attrib.invers); /* invert fg and bg */
-            draw_char_at (vt, x, y, c->ch, attrib);
+            draw_char_at(vt, x, y, c->ch, attrib);
         } else {
-            draw_char_at (vt, x, y, c->ch, c->attrib);
+            draw_char_at(vt, x, y, c->ch, c->attrib);
         }
     }
 }
 
 static void
-spiceterm_refresh (spiceTerm *vt)
+spiceterm_refresh(spiceTerm *vt)
 {
     int x, y, y1;
 
-    // fixme?
-    // 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);
+            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);
 
-    spiceterm_show_cursor (vt, 1);
+    spiceterm_show_cursor(vt, 1);
+}
+
+static void
+spiceterm_clear_screen(spiceTerm *vt)
+{
+    int x, y;
+
+    for (y = 0; y <= vt->height; y++) {
+        int y1 = (vt->y_base + y) % vt->total_height;
+        TextCell *c = &vt->cells[y1 * vt->width];
+        for (x = 0; x < vt->width; x++) {
+            c->ch = ' ';
+            c->attrib = vt->default_attrib;
+            c->attrib.fgcol = vt->cur_attrib.fgcol;
+            c->attrib.bgcol = vt->cur_attrib.bgcol;
+           
+            c++;
+        }
+    }
+
+    spice_screen_clear(vt->screen, 0, 0, vt->screen->primary_width, 
+                       vt->screen->primary_height);
+}
+
+void
+spiceterm_unselect_all(spiceTerm *vt)
+{
+    int x, y, y1;
+
+    y1 = vt->y_displ;
+    for(y = 0; y < vt->total_height; y++) {
+        TextCell *c = vt->cells + y1 * vt->width;
+        for(x = 0; x < vt->width; x++) {
+           if (c->attrib.selected) {
+               c->attrib.selected = 0;
+               if (y < vt->height) {
+                   draw_char_at(vt, x, y, c->ch, c->attrib);
+               }
+           }
+           c++;
+        }
+        if (++y1 == vt->total_height)
+            y1 = 0;
+    }
 }
 
 static void
-spiceterm_scroll_down (spiceTerm *vt, int top, int bottom, int lines)
+spiceterm_scroll_down(spiceTerm *vt, int top, int bottom, int lines)
 {
     if ((top + lines) >= bottom) {
         lines = bottom - top -1;
@@ -226,7 +263,7 @@ spiceterm_scroll_down (spiceTerm *vt, int top, int bottom, int lines)
         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));
+        memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof(TextCell));
     }
 
     for (i = 0; i < lines; i++) {
@@ -249,7 +286,7 @@ spiceterm_scroll_down (spiceTerm *vt, int top, int bottom, int lines)
 }
 
 static void
-spiceterm_scroll_up (spiceTerm *vt, int top, int bottom, int lines, int moveattr)
+spiceterm_scroll_up(spiceTerm *vt, int top, int bottom, int lines, int moveattr)
 {
     if ((top + lines) >= bottom) {
         lines = bottom - top - 1;
@@ -279,7 +316,7 @@ spiceterm_scroll_up (spiceTerm *vt, int top, int bottom, int lines, int moveattr
         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));
+        memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof(TextCell));
     }
 
     for (i = 1; i <= lines; i++) {
@@ -294,7 +331,7 @@ spiceterm_scroll_up (spiceTerm *vt, int top, int bottom, int lines, int moveattr
 }
 
 static void
-spiceterm_virtual_scroll (spiceTerm *vt, int lines)
+spiceterm_virtual_scroll(spiceTerm *vt, int lines)
 {
     if (vt->altbuf || lines == 0) return;
 
@@ -322,13 +359,13 @@ spiceterm_virtual_scroll (spiceTerm *vt, int lines)
         }
     }
 
-    spiceterm_refresh (vt);
+    spiceterm_refresh(vt);
 }
 
 static void
-spiceterm_respond_esc (spiceTerm *vt, const char *esc)
+spiceterm_respond_esc(spiceTerm *vt, const char *esc)
 {
-    int len = strlen (esc);
+    int len = strlen(esc);
     int i;
 
     if (vt->ibuf_count < (IBUFSIZE - 1 - len)) {
@@ -340,17 +377,17 @@ spiceterm_respond_esc (spiceTerm *vt, const char *esc)
 }
 
 static void
-spiceterm_put_lf (spiceTerm *vt)
+spiceterm_put_lf(spiceTerm *vt)
 {
     if (vt->cy + 1 == vt->region_bottom) {
 
         if (vt->altbuf || vt->region_top != 0 || vt->region_bottom != vt->height) {
-            spiceterm_scroll_up (vt, vt->region_top, vt->region_bottom, 1, 1);
+            spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 1);
             return;
         }
 
         if (vt->y_displ == vt->y_base) {
-            spiceterm_scroll_up (vt, vt->region_top, vt->region_bottom, 1, 0);
+            spiceterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 0);
         }
 
         if (vt->y_displ == vt->y_base) {
@@ -384,7 +421,7 @@ spiceterm_put_lf (spiceTerm *vt)
 }
 
 static void
-spiceterm_csi_m (spiceTerm *vt)
+spiceterm_csi_m(spiceTerm *vt)
 {
     int i;
 
@@ -482,7 +519,7 @@ spiceterm_csi_m (spiceTerm *vt)
 }
 
 static void
-spiceterm_save_cursor (spiceTerm *vt)
+spiceterm_save_cursor(spiceTerm *vt)
 {
     vt->cx_saved = vt->cx;
     vt->cy_saved = vt->cy;
@@ -494,7 +531,7 @@ spiceterm_save_cursor (spiceTerm *vt)
 }
 
 static void
-spiceterm_restore_cursor (spiceTerm *vt)
+spiceterm_restore_cursor(spiceTerm *vt)
 {
     vt->cx = vt->cx_saved;
     vt->cy = vt->cy_saved;
@@ -506,7 +543,7 @@ spiceterm_restore_cursor (spiceTerm *vt)
 }
 
 static void
-spiceterm_set_alternate_buffer (spiceTerm *vt, int on_off)
+spiceterm_set_alternate_buffer(spiceTerm *vt, int on_off)
 {
     int x, y;
 
@@ -520,7 +557,7 @@ spiceterm_set_alternate_buffer (spiceTerm *vt, int on_off)
 
         /* alternate buffer & cursor */
 
-        spiceterm_save_cursor (vt);
+        spiceterm_save_cursor(vt);
         /* save screen to altcels */
         for (y = 0; y < vt->height; y++) {
             int y1 = (vt->y_base + y) % vt->total_height;
@@ -532,12 +569,12 @@ spiceterm_set_alternate_buffer (spiceTerm *vt, int on_off)
         /* clear screen */
         for (y = 0; y <= vt->height; y++) {
             for (x = 0; x < vt->width; x++) {
-                spiceterm_clear_xy (vt, x, y);
+                //     spiceterm_clear_xy(vt, x, y);
             }
         }
-        
+
     } else {
-        
+
         if (vt->altbuf == 0) return;
 
         vt->altbuf = 0;
@@ -550,14 +587,14 @@ spiceterm_set_alternate_buffer (spiceTerm *vt, int on_off)
             }
         }
 
-        spiceterm_restore_cursor (vt);
+        spiceterm_restore_cursor(vt);
     }
 
-    spiceterm_refresh (vt);
+    spiceterm_refresh(vt);
 }
 
 static void
-spiceterm_set_mode (spiceTerm *vt, int on_off)
+spiceterm_set_mode(spiceTerm *vt, int on_off)
 {
     int i;
 
@@ -565,29 +602,32 @@ spiceterm_set_mode (spiceTerm *vt, int on_off)
         if (vt->esc_ques) {          /* DEC private modes set/reset */
             switch(vt->esc_buf[i]) {
             case 10:                   /* X11 mouse reporting on/off */
-            case 1000:
+            case 1000:                 /* SET_VT200_MOUSE */
+            case 1002:                 /* xterm SET_BTN_EVENT_MOUSE */
                 vt->report_mouse = on_off;
                 break;
             case 1049:         /* start/end special app mode (smcup/rmcup) */
                 spiceterm_set_alternate_buffer (vt, on_off);
                 break;
             case 25:                   /* Cursor on/off */
-            case 9:                   /* X10 mouse reporting 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 */
+            //g_assert_not_reached();
+
             /* fixme: implement me */
         }
     }
 }
 
 static void
-spiceterm_gotoxy (spiceTerm *vt, int x, int y)
+spiceterm_gotoxy(spiceTerm *vt, int x, int y)
 {
     /* verify all boundaries */
 
@@ -612,17 +652,37 @@ spiceterm_gotoxy (spiceTerm *vt, int x, int y)
     vt->cy = y;
 }
 
+static void
+debug_print_escape_buffer(spiceTerm *vt, const char *func, const char *prefix,
+                          const char *qes, gunichar2 ch)
+{
+    if (debug >=1 ) {
+        if (vt->esc_count == 0) {
+            printf("%s:%s ESC[%s%c\n", func, prefix, qes, ch);
+        } else if (vt->esc_count == 1) {
+            printf("%s:%s ESC[%s%d%c\n", func, prefix, qes, vt->esc_buf[0], ch);
+        } else {
+            int i;
+            printf("%s:%s ESC[%s%d", func, prefix, qes, vt->esc_buf[0]);
+            for (i = 1; i < vt->esc_count; i++) {
+                printf(";%d",  vt->esc_buf[i]);
+            }
+            printf("%c\n", ch);
+        }
+    }
+}
+
 enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
        EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
        ESpalette, ESidquery, ESosc1, ESosc2};
 
 static void
-spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
+spiceterm_putchar(spiceTerm *vt, gunichar2 ch)
 {
     int x, y, i, c;
 
     if (debug && !vt->tty_state) {
-        DPRINTF(1, "%s: CHAR:%2d: %4x '%c' (cur_enc %d) %d %d", __func__,
+        DPRINTF(1, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d",
                 vt->tty_state, ch, ch, vt->cur_enc, vt->cx, vt->cy);
     }
 
@@ -640,10 +700,10 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
             vt->tty_state = ESpercent;
             break;
         case '7':
-            spiceterm_save_cursor (vt);
+            spiceterm_save_cursor(vt);
             break;
         case '8':
-            spiceterm_restore_cursor (vt);
+            spiceterm_restore_cursor(vt);
             break;
         case '(':
             vt->tty_state = ESsetG0; // SET G0
@@ -654,7 +714,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
         case 'M':
             /* cursor up (ri) */
             if (vt->cy == vt->region_top)
-                spiceterm_scroll_down (vt, vt->region_top, vt->region_bottom, 1);
+                spiceterm_scroll_down(vt, vt->region_top, vt->region_bottom, 1);
             else if (vt->cy > 0) {
                 vt->cy--;
             }
@@ -666,7 +726,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
             /* appl. keypad - ignored */
             break;
         default:
-            DPRINTF(1, "%s: got unhandled ESC%c  %d", __func__, ch, ch);
+            DPRINTF(1, "got unhandled ESC%c  %d", ch, ch);
             break;
         }
         break;
@@ -694,7 +754,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
             vt->tty_state = ESosc1;
             break;
         default:
-            DPRINTF(1, "%s: got unhandled OSC %c", __func__, ch);
+            DPRINTF(1, "got unhandled OSC %c", ch);
             vt->tty_state = ESnormal;
             break;
         }
@@ -704,7 +764,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
         if (ch == ';') {
             vt->tty_state = ESosc2;
         } else {
-            DPRINTF(1, "%s: got illegal OSC sequence", __func__);
+            DPRINTF(1, "got illegal OSC sequence");
         }
         break;
     case ESosc2:
@@ -714,7 +774,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
             vt->osc_textbuf[i++] = ch;
             vt->osc_textbuf[i] = 0;
         } else {
-            DPRINTF(1, "%s: OSC:%c:%s", __func__, vt->osc_cmd, vt->osc_textbuf);
+            DPRINTF(1, "OSC:%c:%s", vt->osc_cmd, vt->osc_textbuf);
             vt->tty_state = ESnormal;
         }
         break;
@@ -736,7 +796,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
                   cmap->data.bytes[i] += vt->esc_buf[j];
                 */
                 //set_palette(vc); ?
-                
+
                 vt->tty_state = ESnormal;
             }
         } else
@@ -750,7 +810,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
         vt->esc_count = 0;
         vt->esc_has_par = 0;
         vt->tty_state = ESgetpars;
-        
+
         if (ch == '>') {
             vt->tty_state = ESidquery;
             break;
@@ -776,39 +836,27 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
             vt->tty_state = ESgotpars;
         }
     case ESgotpars:
-        
+
         vt->tty_state = ESnormal;
 
         char *qes = vt->esc_ques ? "?" : "";
 
         if (debug) {
-            //fixme:
-            if (vt->esc_count == 0) {
-                DPRINTF(1, "%s: ESC[%s%c", __func__, qes, ch);
-            } else if (vt->esc_count == 1) {
-                DPRINTF(1, "%s: ESC[%s%d%c\n", __func__, qes, vt->esc_buf[0], ch);
-            } else {
-                int i;
-                printf("ESC[%s%d", qes, vt->esc_buf[0]);
-                for (i = 1; i < vt->esc_count; i++) {
-                    printf(";%d",  vt->esc_buf[i]);
-                }
-                printf("%c\n", ch);
-            }
+            debug_print_escape_buffer(vt, __func__, "", qes, ch);
         }
 
         switch (ch) {
         case 'h':
-            spiceterm_set_mode (vt, 1);
+            spiceterm_set_mode(vt, 1);
             break;
         case 'l':
-            spiceterm_set_mode (vt, 0);
+            spiceterm_set_mode(vt, 0);
             break;
         case 'm':
             if (!vt->esc_count) {
                 vt->esc_count++; // default parameter 0
             }
-            spiceterm_csi_m (vt);
+            spiceterm_csi_m(vt);
             break;
         case 'n':
             /* report cursor position */
@@ -859,16 +907,16 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
         case 'G':
         case '`':
             /* move cursor to column */
-            spiceterm_gotoxy (vt, vt->esc_buf[0] - 1, vt->cy);
+            spiceterm_gotoxy(vt, vt->esc_buf[0] - 1, vt->cy);
             break;
         case 'd':
             /* move cursor to row */
-            spiceterm_gotoxy (vt, vt->cx , vt->esc_buf[0] - 1);
+            spiceterm_gotoxy(vt, vt->cx , vt->esc_buf[0] - 1);
             break;
         case 'f':
         case 'H':
             /* move cursor to row, column */
-            spiceterm_gotoxy (vt, vt->esc_buf[1] - 1,  vt->esc_buf[0] - 1);
+            spiceterm_gotoxy(vt, vt->esc_buf[1] - 1,  vt->esc_buf[0] - 1);
             break;
         case 'J':
             switch (vt->esc_buf[0]) {
@@ -890,17 +938,13 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
                         if (y == vt->cy && x > vt->cx) {
                             break;
                         }
-                        spiceterm_clear_xy (vt, x, y);
+                        spiceterm_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++) {
-                        spiceterm_clear_xy (vt, x, y);
-                    }
-                }
+                spiceterm_clear_screen(vt);
                 break;
             }
             break;
@@ -909,19 +953,19 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
             case 0:
                 /* clear to eol */
                 for(x = vt->cx; x < vt->width; x++) {
-                    spiceterm_clear_xy (vt, x, vt->cy);
+                    spiceterm_clear_xy(vt, x, vt->cy);
                 }
                 break;
             case 1:
                 /* clear from beginning of line */
                 for (x = 0; x <= vt->cx; x++) {
-                    spiceterm_clear_xy (vt, x, vt->cy);
+                    spiceterm_clear_xy(vt, x, vt->cy);
                 }
                 break;
             case 2:
                 /* clear entire line */
                 for(x = 0; x < vt->width; x++) {
-                    spiceterm_clear_xy (vt, x, vt->cy);
+                    spiceterm_clear_xy(vt, x, vt->cy);
                 }
                 break;
             }
@@ -929,41 +973,41 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
         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;
 
-            spiceterm_scroll_down (vt, vt->cy, vt->region_bottom, c);
+            spiceterm_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;
 
-            spiceterm_scroll_up (vt, vt->cy, vt->region_bottom, c, 1);
+            spiceterm_scroll_up(vt, vt->cy, vt->region_bottom, c, 1);
             break;
         case 'T':
             /* scroll down */
             c = vt->esc_buf[0];
             if (!c) c = 1;
-            spiceterm_scroll_down (vt, vt->region_top, vt->region_bottom, c);
+            spiceterm_scroll_down(vt, vt->region_top, vt->region_bottom, c);
             break;
         case 'S':
             /* scroll up */
             c = vt->esc_buf[0];
             if (!c) c = 1;
-            spiceterm_scroll_up (vt, vt->region_top, vt->region_bottom, c, 1);
+            spiceterm_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)
@@ -974,29 +1018,29 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
                 TextCell *dst = &vt->cells[y1 * vt->width + x];
                 TextCell *src = dst + c;
                 *dst = *src;
-                spiceterm_update_xy (vt, x + c, vt->cy);
+                spiceterm_update_xy(vt, x + c, vt->cy);
                 src->ch = ' ';
                 src->attrib = vt->default_attrib;
-                spiceterm_update_xy (vt, x, vt->cy);
+                spiceterm_update_xy(vt, x, vt->cy);
             }
             break;
         case 's':
             /* save cursor position */
-            spiceterm_save_cursor (vt);
+            spiceterm_save_cursor(vt);
             break;
         case 'u':
             /* restore cursor position */
-            spiceterm_restore_cursor (vt);
+            spiceterm_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++) {
-                spiceterm_clear_xy (vt, vt->cx + i, vt->cy);
+                spiceterm_clear_xy(vt, vt->cx + i, vt->cy);
             }
             break;
         case '@':
@@ -1006,7 +1050,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
                 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];
@@ -1015,7 +1059,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
                 spiceterm_update_xy (vt, x + c, vt->cy);
                 src->ch = ' ';
                 src->attrib = vt->cur_attrib;
-                spiceterm_update_xy (vt, x, vt->cy);
+                spiceterm_update_xy(vt, x, vt->cy);
             }
 
             break;
@@ -1032,25 +1076,13 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
                 vt->region_bottom = vt->esc_buf[1];
                 vt->cx = 0;
                 vt->cy = vt->region_top;
-                DPRINTF(1, "%s: set region %d %d", __func__, vt->region_top, vt->region_bottom);
+                DPRINTF(1, "set region %d %d", vt->region_top, vt->region_bottom);
             }
 
             break;
         default:
             if (debug) {
-                // fixme
-                if (vt->esc_count == 0) {
-                    DPRINTF(1, "%s: unhandled escape ESC[%s%c", __func__,  qes, ch);
-                } else if (vt->esc_count == 1) {
-                    DPRINTF(1, "%s: unhandled escape ESC[%s%d%c\n", __func__, qes, vt->esc_buf[0], ch);
-                } else {
-                    int i;
-                    printf("unhandled escape ESC[%s%d", qes, vt->esc_buf[0]);
-                    for (i = 1; i < vt->esc_count; i++) {
-                        printf(";%d",  vt->esc_buf[i]);
-                    }
-                    printf("%c\n", ch);
-                }
+                debug_print_escape_buffer(vt, __func__, " unhandled escape", qes, ch);
             }
             break;
         }
@@ -1067,7 +1099,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
             vt->g0enc = IBMPC_MAP;
         else if (ch == 'K')
             vt->g0enc = USER_MAP;
-        
+
         if (vt->charset == 0)
             vt->cur_enc = vt->g0enc;
 
@@ -1092,9 +1124,9 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
         vt->tty_state = ESnormal;
 
         if (ch == 'c') {
-            DPRINTF(1, "%s: ESC[>c   Query term ID", __func__);
-            spiceterm_respond_esc (vt, TERMIDCODE);
-        }
+            DPRINTF(1, "ESC[>c   Query term ID");
+            spiceterm_respond_esc(vt, TERMIDCODE);
+         }
         break;
     case ESpercent:
         vt->tty_state = ESnormal;
@@ -1125,7 +1157,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
         case 9:  /* tabspace */
             if (vt->cx + (8 - (vt->cx % 8)) > vt->width) {
                 vt->cx = 0;
-                spiceterm_put_lf (vt);
+                spiceterm_put_lf(vt);
             } else {
                 vt->cx = vt->cx + (8 - (vt->cx % 8));
             }
@@ -1133,7 +1165,7 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
         case 10:  /* LF,*/
         case 11:  /* VT */
         case 12:  /* FF */
-            spiceterm_put_lf (vt);
+            spiceterm_put_lf(vt);
             break;
         case 13:  /* carriage return */
             vt->cx = 0;
@@ -1163,14 +1195,14 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
             if (vt->cx >= vt->width) {
                 /* line wrap */
                 vt->cx = 0;
-                spiceterm_put_lf (vt);
+                spiceterm_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;
-            spiceterm_update_xy (vt, vt->cx, vt->cy);
+            spiceterm_update_xy(vt, vt->cx, vt->cy);
             vt->cx++;
             break;
         }
@@ -1179,11 +1211,11 @@ spiceterm_putchar (spiceTerm *vt, gunichar2 ch)
 }
 
 static int
-spiceterm_puts (spiceTerm *vt, const char *buf, int len)
+spiceterm_puts(spiceTerm *vt, const char *buf, int len)
 {
     gunichar2 tc;
 
-    spiceterm_show_cursor (vt, 0);
+    spiceterm_show_cursor(vt, 0);
 
     while (len) {
         unsigned char c = *buf;
@@ -1242,18 +1274,18 @@ spiceterm_puts (spiceTerm *vt, const char *buf, int len)
                 tc = c;
             }
         }
-        
-        spiceterm_putchar (vt, tc);
+
+        spiceterm_putchar(vt, tc);
     }
 
-    spiceterm_show_cursor (vt, 1);
-    
+    spiceterm_show_cursor(vt, 1);
+
     return len;
 }
 
 /* fixme:
 void
-spiceterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl)
+spiceterm_set_xcut_text(char* str, int len, struct _rfbClientRec* cl)
 {
   spiceTerm *vt =(spiceTerm *)cl->screen->screenData;
 
@@ -1267,197 +1299,72 @@ spiceterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl)
   vt->selection_len = len;
 }
 */
-/*
+
 static void
-mouse_report (spiceTerm *vt, int butt, int mrx, int mry)
+spiceterm_update_watch_mask(spiceTerm *vt, gboolean writable)
 {
-  char buf[8];
+    g_assert(vt != NULL);
 
-  sprintf (buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
-          (char)('!' + mry));
+    int mask = SPICE_WATCH_EVENT_READ;
+
+    if (writable) {
+        mask |= SPICE_WATCH_EVENT_WRITE;
+    }
 
-  spiceterm_respond_esc (vt, buf);
+    vt->screen->core->watch_update_mask(vt->screen->mwatch, mask);
 }
-*/
 
-void
-spiceterm_toggle_marked_cell (spiceTerm *vt, int pos)
+static void
+mouse_report(spiceTerm *vt, int butt, int mrx, int mry)
 {
+    char buf[8];
 
-/* fixme:
-  int x= (pos%vt->width)*8;
-  int y= (pos/vt->width)*16;
-
-  int i,j;
-  rfbScreenInfoPtr s=vt->screen;
+    sprintf(buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
+             (char)('!' + mry));
 
-  char *b = s->frameBuffer+y*s->width+x;
+    spiceterm_respond_esc(vt, buf);
 
-  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);
-    }
-  }
-*/
+    spiceterm_update_watch_mask(vt, TRUE);
 }
 
-/* fixme:
-
-void
-spiceterm_pointer_event (int buttonMask, int x, int y, rfbClientPtr cl)
+static void
+spiceterm_respond_unichar2(spiceTerm *vt, gunichar2 uc)
 {
+    if (vt->utf8) {
+        gchar buf[10];
+        gint len = g_unichar_to_utf8(uc, buf);
 
-  spiceTerm *vt =(spiceTerm *)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;
-       spiceterm_refresh (vt);
-      }
-    }
-    button2_released = 0;
-  } else {
-    button2_released = 1;
-  }
-
-  if (buttonMask & 1) {
-    int pos = cy*vt->width + cx;
-
-    // code borrowed from libvncserver (VNCconsole.c)
-
-    if (!vt->mark_active) {
-
-      vt->mark_active = 1;
-      sel_start_pos = sel_end_pos = pos;
-      spiceterm_toggle_marked_cell (vt, pos);
-
+        if (len > 0) {
+            if ((vt->ibuf_count + len) < IBUFSIZE) {
+                int i;
+                for (i = 0; i < len; i++) {
+                    vt->ibuf[vt->ibuf_count++] = buf[i];
+                }
+            } else {
+                fprintf(stderr, "warning: input buffer overflow\n");
+            }
+        }
     } 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) {
-         spiceterm_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 = (gunichar2 *)malloc (len*sizeof (gunichar2));
-    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) {
-      spiceterm_toggle_marked_cell (vt, sel_start_pos++);
+        if ((vt->ibuf_count + 1) < IBUFSIZE) {
+            vt->ibuf[vt->ibuf_count++] = (char)uc;
+        } else {
+            fprintf(stderr, "warning: input buffer overflow\n");
+        }
     }
-
-  }
-
-  rfbDefaultPtrAddEvent (buttonMask, x, y, cl);
-
 }
-*/
 
-static void my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
-{
-    // spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
-
-    /* we no not need this */
-
-    return;
-}
-
-static void my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags)
+static void
+spiceterm_push_keysym(spiceTerm *vt, uint32_t keySym, uint32_t flags)
 {
-    spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
     static int control = 0;
     static int shift = 0;
     char *esc = NULL;
 
-    guint uc = 0;
+    gunichar2 uc = 0;
 
-    DPRINTF(1, "%s: flags=%d keySym=%08x", __func__,  flags, keySym);
+    DPRINTF(1, "flags=%d keySym=%08x", flags, keySym);
 
-    if (flags & 1) {
+    if (flags & SPICE_KEYBOARD_FLAG_DOWN) {
         if (keySym == GDK_KEY_Shift_L || keySym == GDK_KEY_Shift_R) {
             shift = 1;
         } if (keySym == GDK_KEY_Control_L || keySym == GDK_KEY_Control_R) {
@@ -1550,37 +1457,24 @@ static void my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags
                 }
             }
 
-            DPRINTF(1, "%s: escape=%s unicode=%08x\n", __func__, esc, uc);
+            DPRINTF(1, "escape=%s unicode=%08x\n", esc, uc);
 
             if (vt->y_displ != vt->y_base) {
                 vt->y_displ = vt->y_base;
-                spiceterm_refresh (vt);
+                spiceterm_refresh(vt);
             }
 
             if (esc) {
                 spiceterm_respond_esc(vt, esc);
             } else if (uc > 0) {
-                if (vt->utf8) {
-                    gchar buf[10];
-                    gint len = g_unichar_to_utf8(uc, buf);
-
-                    if (len > 0) {
-                        int i;
-                        for (i = 0; i < len; i++) {
-                            vt->ibuf[vt->ibuf_count++] = buf[i];
-                        }
-                    }
-                } else {
-                    vt->ibuf[vt->ibuf_count++] = (char)uc;
-                }
+                spiceterm_respond_unichar2(vt, uc);
             }
         }
     }
 
-
 ret:
 
-    if (flags & 2) { // UP
+    if (flags & SPICE_KEYBOARD_FLAG_UP) {
         if (keySym == GDK_KEY_Shift_L || keySym == GDK_KEY_Shift_R) {
             shift = 0;
         } else if (keySym == GDK_KEY_Control_L || keySym == GDK_KEY_Control_R) {
@@ -1588,53 +1482,552 @@ ret:
         }
     }
 
-    vt->screen->core->watch_update_mask(vt->screen->mwatch,
-                                        SPICE_WATCH_EVENT_READ|SPICE_WATCH_EVENT_WRITE);
+    spiceterm_update_watch_mask(vt, TRUE);
 }
 
-static uint8_t my_kbd_get_leds(SpiceKbdInstance *sin)
+static uint8_t
+my_kbd_get_leds(SpiceKbdInstance *sin)
 {
     return 0;
 }
 
+static void
+my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
+{
+    // spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
+
+    /* we no not need this */
+
+    return;
+}
+
+static void
+my_kbd_push_x11_keysym(SpiceKbdInstance *sin, uint32_t keysym, uint32_t flags,
+                       uint8_t code_len, uint8_t *code)
+{
+    spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
+
+    DPRINTF(1, "flags=%d keySym=%08x code_len=%d", flags, keysym, code_len);
+
+    if (keysym) {
+        spiceterm_push_keysym(vt, keysym, flags);
+    }
+
+    return;
+}
+
 static SpiceKbdInterface my_keyboard_sif = {
-    .base.type          = SPICE_INTERFACE_KEYBOARD ,
+    .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_keyval        = my_kbd_push_keyval,
     .push_scan_freg     = my_kbd_push_key,
     .get_leds           = my_kbd_get_leds,
+    .push_x11_keysym    = my_kbd_push_x11_keysym,
 };
 
-spiceTerm *
-create_spiceterm (int argc, char** argv, int maxx, int maxy)
+
+/* vdagent interface - to get mouse/clipboarde support */
+
+#define VDAGENT_WBUF_SIZE (1024*50)
+static unsigned char vdagent_write_buffer[VDAGENT_WBUF_SIZE];
+static int vdagent_write_buffer_pos = 0;
+static int agent_owns_clipboard[256] = { 0, };
+
+static void
+spiceterm_clear_selection(spiceTerm *vt)
 {
+    DPRINTF(1, "mark_active = %d", vt->mark_active);
+
+    vt->mark_active = 0;
+    if (vt->selection) free (vt->selection);
+    vt->selection = NULL;
+    spiceterm_unselect_all(vt);
+}
+static void
+spiceterm_motion_event(spiceTerm *vt, uint32_t x, uint32_t y, uint32_t buttons)
+{
+    DPRINTF(1, "mask=%08x x=%d y=%d", buttons, x ,y);
+
+    static int last_mask = 0;
+    static int sel_start_pos = 0;
+    static int sel_end_pos = 0;
+    static int button2_released = 1;
+
     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 && buttons != last_mask) {
+        last_mask = buttons;
+        if (buttons & 2) {
+            mouse_report(vt, 0, cx, cy);
+        }
+        if (buttons & 4) {
+            mouse_report (vt, 1, cx, cy);
+        }
+        if (buttons & 8) {
+            mouse_report(vt, 2, cx, cy);
+        }
+        if(!buttons) {
+            mouse_report(vt, 3, cx, cy);
+        }
+    }
 
-    SpiceScreen *spice_screen;
+    if (buttons & 4) {
 
-    SpiceCoreInterface *core = basic_event_loop_init();
-    spice_screen = spice_screen_new(core);
-    //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
-    spice_screen_add_display_interface(spice_screen);
-    spice_screen_add_agent_interface(spice_screen->server);
+        if(button2_released) {
 
-    spiceTerm *vt = (spiceTerm *)calloc (sizeof(spiceTerm), 1);
+            if (agent_owns_clipboard[VD_AGENT_CLIPBOARD_SELECTION_PRIMARY]) {
+                if (vt->selection) {
+                    int i;
+                    for(i = 0; i < vt->selection_len; i++) {
+                        spiceterm_respond_unichar2(vt, vt->selection[i]);
+                    }
+                    spiceterm_update_watch_mask(vt, TRUE);
+                    if (vt->y_displ != vt->y_base) {
+                        vt->y_displ = vt->y_base;
+                        spiceterm_refresh(vt);
+                    }
+                }
+            } else {
+                vdagent_request_clipboard(vt, VD_AGENT_CLIPBOARD_SELECTION_PRIMARY);
+            } 
+        }
 
-    vt->keyboard_sin.base.sif = &my_keyboard_sif.base;
-    spice_server_add_interface(spice_screen->server, &vt->keyboard_sin.base);
+        button2_released = 0;
+    } else {
+        button2_released = 1;
+    }
+
+    if (buttons & 2) {
+        int pos = cy*vt->width + cx;
+
+        // code borrowed from libvncserver (VNCconsole.c)
+
+        if (!vt->mark_active) {
+
+            spiceterm_unselect_all(vt);
+
+            vt->mark_active = 1;
+            sel_start_pos = sel_end_pos = pos;
+            spiceterm_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) {
+                    spiceterm_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;
 
-    // screen->setXCutText = spiceterm_set_xcut_text;
-    // screen->ptrAddEvent = spiceterm_pointer_event;
-    // screen->newClientHook = new_client;
-    // screen->desktopName = "SPICE Command Terminal";
+        if (vt->selection) free (vt->selection);
+        vt->selection = (gunichar2 *)malloc (len*sizeof(gunichar2));
+        vt->selection_len = len;
 
-    vt->maxx = spice_screen->width;
-    vt->maxy = spice_screen->height;
+        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;
+            c++;
+        }
+
+        DPRINTF(1, "selection length = %d", vt->selection_len);
+
+        vdagent_grab_clipboard(vt, VD_AGENT_CLIPBOARD_SELECTION_PRIMARY);
+    }
+}
+
+static void  
+vdagent_reply(spiceTerm *vt, uint32_t type, uint32_t error)
+{
+    uint32_t size;
+    
+    size = sizeof(VDAgentReply);
+
+    int msg_size =  sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
+    g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE);
+
+    unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos;
+    vdagent_write_buffer_pos += msg_size;
+
+    memset(buf, 0, msg_size);
+
+    VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
+    VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
+    VDAgentReply *reply = (VDAgentReply *)&msg[1];
+    reply->type = type;
+    reply->error = error;
 
-    vt->width = vt->maxx / 8;
-    vt->height = vt->maxy / 16;
+    hdr->port = VDP_CLIENT_PORT;
+    hdr->size = sizeof(VDAgentMessage) + size;
+
+    msg->protocol = VD_AGENT_PROTOCOL;
+    msg->type = VD_AGENT_REPLY;
+    msg->opaque = 0;
+    msg->size = size;
+
+    spice_server_char_device_wakeup(&vt->vdagent_sin);
+}
+
+static void
+dump_message(unsigned char *buf, int size)
+{
+    int i;
+
+    for (i = 0; i < size; i++) {
+        printf("%d  %02X\n", i, buf[i]);
+    }
+
+    // exit(0);
+}
+
+static void vdagent_send_capabilities(spiceTerm *vt, uint32_t request)
+{
+    VDAgentAnnounceCapabilities *caps;
+    uint32_t size;
+
+    size = sizeof(*caps) + VD_AGENT_CAPS_BYTES;
+    caps = calloc(1, size);
+    g_assert(caps != NULL);
+
+    caps->request = request;
+    VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE);
+    VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
+    VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY);
+    VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
+    VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION);
+    VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG);
+    VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_GUEST_LINEEND_LF);
+
+    int msg_size =  sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
+    g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE);
+
+    unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos;
+    vdagent_write_buffer_pos += msg_size;
+
+    VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
+    VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
+    hdr->port = VDP_CLIENT_PORT;
+    hdr->size = sizeof(VDAgentMessage) + size;
+    msg->protocol = VD_AGENT_PROTOCOL;
+    msg->type = VD_AGENT_ANNOUNCE_CAPABILITIES;
+    msg->opaque = 0;
+    msg->size = size;
+
+    memcpy(buf + sizeof(VDIChunkHeader) + sizeof(VDAgentMessage), (uint8_t *)caps, size);
+
+    if (0) dump_message(buf, msg_size);
+
+    spice_server_char_device_wakeup(&vt->vdagent_sin);
+
+    free(caps);
+}
+
+static void vdagent_grab_clipboard(spiceTerm *vt, uint8_t selection)
+{
+    uint32_t size;
+    
+    agent_owns_clipboard[selection] = 1;
+
+    size = 8;
+
+    int msg_size =  sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
+    g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE);
+
+    unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos;
+    vdagent_write_buffer_pos += msg_size;
+
+    memset(buf, 0, msg_size);
+
+    VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
+    VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
+    uint8_t *grab = (uint8_t *)&msg[1];
+    *((uint8_t *)grab) = selection;
+    *((uint32_t *)(grab + 4)) = VD_AGENT_CLIPBOARD_UTF8_TEXT;
+
+    hdr->port = VDP_CLIENT_PORT;
+    hdr->size = sizeof(VDAgentMessage) + size;
+
+    msg->protocol = VD_AGENT_PROTOCOL;
+    msg->type = VD_AGENT_CLIPBOARD_GRAB;
+    msg->opaque = 0;
+    msg->size = size;
+
+    if (0) dump_message(buf, msg_size);
+
+    spice_server_char_device_wakeup(&vt->vdagent_sin);
+}
+
+static void vdagent_request_clipboard(spiceTerm *vt, uint8_t selection)
+{
+    uint32_t size;
+    
+    size = 4 + sizeof(VDAgentClipboardRequest);
+
+    int msg_size =  sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
+    g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE);
+
+    unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos;
+    vdagent_write_buffer_pos += msg_size;
+
+    memset(buf, 0, msg_size);
+
+    VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
+    VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
+    uint8_t *data = (uint8_t *)&msg[1];
+    *((uint32_t *)data) = 0;
+    data[0] = selection;
+    ((uint32_t *)data)[1] = VD_AGENT_CLIPBOARD_UTF8_TEXT;
+
+    hdr->port = VDP_CLIENT_PORT;
+    hdr->size = sizeof(VDAgentMessage) + size;
+
+    msg->protocol = VD_AGENT_PROTOCOL;
+    msg->type = VD_AGENT_CLIPBOARD_REQUEST;
+    msg->opaque = 0;
+    msg->size = size;
+
+    if (0) dump_message(buf, msg_size);
+
+    spice_server_char_device_wakeup(&vt->vdagent_sin);
+}
+
+static void vdagent_send_clipboard(spiceTerm *vt, uint8_t selection)
+{
+    uint32_t size;
+    
+    if (selection != VD_AGENT_CLIPBOARD_SELECTION_PRIMARY) {
+        fprintf(stderr, "clipboard select %d is not supported\n", selection);
+        return;
+    }
+
+    gchar *sel_data;
+    glong sel_len;
+    if (vt->utf8) {
+        sel_data = g_utf16_to_utf8(vt->selection, vt->selection_len, NULL, &sel_len, NULL);
+    } else {
+        sel_len = vt->selection_len;
+        sel_data = g_malloc(sel_len);
+        int i;
+        for (i = 0; i < sel_len; i++) { sel_data[i] =  (char)vt->selection[i]; }
+        sel_data[sel_len] = 0;
+    }
+
+    size = 8 + sel_len;
+
+    int msg_size =  sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
+    g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE);
+
+    unsigned char *buf = vdagent_write_buffer + vdagent_write_buffer_pos;
+    vdagent_write_buffer_pos += msg_size;
+
+    memset(buf, 0, msg_size);
+   
+    VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
+    VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
+    uint8_t *data = (uint8_t *)&msg[1];
+    *((uint8_t *)data) = selection;
+    data += 4;
+    *((uint32_t *)data) = VD_AGENT_CLIPBOARD_UTF8_TEXT;
+    data += 4;
+
+    memcpy(data, sel_data, sel_len);
+    g_free(sel_data);
+
+    hdr->port = VDP_CLIENT_PORT;
+    hdr->size = sizeof(VDAgentMessage) + size;
+
+    msg->protocol = VD_AGENT_PROTOCOL;
+    msg->type = VD_AGENT_CLIPBOARD;
+    msg->opaque = 0;
+    msg->size = size;
+
+    spice_server_char_device_wakeup(&vt->vdagent_sin);
+}
+
+static int
+vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
+{
+    spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, vdagent_sin);
+
+    VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
+    VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
+
+    //g_assert(hdr->port == VDP_SERVER_PORT);
+    g_assert(msg->protocol == VD_AGENT_PROTOCOL);
+
+    DPRINTF(1, "%d %d %d %d", len, hdr->port, msg->protocol, msg->type);
+
+    switch (msg->type) {
+    case VD_AGENT_MOUSE_STATE: { 
+        VDAgentMouseState *info = (VDAgentMouseState *)&msg[1];
+        spiceterm_motion_event(vt, info->x, info->y, info->buttons);
+        break;
+    }
+    case VD_AGENT_ANNOUNCE_CAPABILITIES: {
+        VDAgentAnnounceCapabilities *caps = (VDAgentAnnounceCapabilities *)&msg[1];
+        DPRINTF(1, "VD_AGENT_ANNOUNCE_CAPABILITIES %d", caps->request);
+        int i;
+        
+        int caps_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(hdr->size);
+        for (i = 0; i < VD_AGENT_END_CAP; i++) {
+            DPRINTF(1, "CAPABILITIES %d %d", i, VD_AGENT_HAS_CAPABILITY(caps->caps, caps_size, i));
+        }
+
+        vdagent_send_capabilities(vt, 0);
+        break;
+    }
+    case VD_AGENT_CLIPBOARD_GRAB: {
+        VDAgentClipboardGrab *grab = (VDAgentClipboardGrab *)&msg[1];
+        uint8_t selection = *((uint8_t *)grab);
+        DPRINTF(1, "VD_AGENT_CLIPBOARD_GRAB %d", selection);
+        agent_owns_clipboard[selection] = 0;
+        spiceterm_clear_selection(vt);
+        break;
+    }
+    case VD_AGENT_CLIPBOARD_REQUEST: {
+        uint8_t *req = (uint8_t *)&msg[1];
+        uint8_t selection = *((uint8_t *)req);
+        uint32_t type = *((uint32_t *)(req + 4));
+
+        DPRINTF(1, "VD_AGENT_CLIPBOARD_REQUEST %d %d", selection, type);
+
+        vdagent_send_clipboard(vt, selection);
+        
+        break;
+    }
+    case VD_AGENT_CLIPBOARD: {
+        uint8_t *data = (uint8_t *)&msg[1];
+        uint8_t selection = data[0];
+        uint32_t type = *(uint32_t *)(data + 4);
+        int size = msg->size - 8;
+        DPRINTF(1, "VD_AGENT_CLIPBOARD %d %d %d", selection, type, size);
+
+        if (type == VD_AGENT_CLIPBOARD_UTF8_TEXT) {
+            int i;
+            for (i = 0; i < size; i++) {
+                if ((vt->ibuf_count + 1) < IBUFSIZE) {
+                    vt->ibuf[vt->ibuf_count++] = *(char *)(data + 8 + i);
+                } else {
+                    fprintf(stderr, "warning: input buffer overflow\n");
+                }
+            }
+            spiceterm_update_watch_mask(vt, TRUE);
+        }
+        break;
+    }
+    case VD_AGENT_CLIPBOARD_RELEASE: {
+        uint8_t *data = (uint8_t *)&msg[1];
+        uint8_t selection = data[0];
+        
+        DPRINTF(0, "VD_AGENT_CLIPBOARD_RELEASE %d", selection);
+     
+        break;
+    }
+    case VD_AGENT_MONITORS_CONFIG: {
+        VDAgentMonitorsConfig *list = (VDAgentMonitorsConfig *)&msg[1];
+        g_assert(list->num_of_monitors > 0);
+        DPRINTF(0, "VD_AGENT_MONITORS_CONFIG %d %d %d", list->num_of_monitors, 
+                list->monitors[0].width, list->monitors[0].height);
+        
+        spiceterm_resize(vt, list->monitors[0].width, list->monitors[0].height);
+
+        vdagent_reply(vt, VD_AGENT_MONITORS_CONFIG, VD_AGENT_SUCCESS);
+        break;
+    }
+    default:
+        DPRINTF(0, "got uknown vdagent message type %d\n", msg->type);
+    }
+
+    return len;
+}
+
+static int
+vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
+{
+    DPRINTF(1, "%d %d", len,  vdagent_write_buffer_pos);
+    g_assert(len >= 8);
+
+    if (!vdagent_write_buffer_pos) {
+        return 0;
+    }
+     
+    int size = (len >= vdagent_write_buffer_pos) ? vdagent_write_buffer_pos : len;
+    memcpy(buf, vdagent_write_buffer, size);
+    if (size < vdagent_write_buffer_pos) {
+        memmove(vdagent_write_buffer, vdagent_write_buffer + size, 
+                vdagent_write_buffer_pos - size);
+    }
+    vdagent_write_buffer_pos -= size;
+
+    DPRINTF(1, "RET %d %d", size,  vdagent_write_buffer_pos);
+    return size;
+}
+
+static void
+vmc_state(SpiceCharDeviceInstance *sin, int connected)
+{
+    /* IGNORE */
+}
+
+static SpiceCharDeviceInterface my_vdagent_sif = {
+    .base.type          = SPICE_INTERFACE_CHAR_DEVICE,
+    .base.description   = "spice virtual channel char device",
+    .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
+    .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
+    .state              = vmc_state,
+    .write              = vmc_write,
+    .read               = vmc_read,
+};
+
+static void
+init_spiceterm(spiceTerm *vt, uint32_t width, uint32_t height)
+{
+    int i;
+
+    g_assert(vt != NULL);
+    g_assert(vt->screen != NULL);
+
+    vt->width = width / 8;
+    vt->height = height / 16;
 
     vt->total_height = vt->height * 20;
     vt->scroll_height = 0;
@@ -1660,43 +2053,126 @@ create_spiceterm (int argc, char** argv, int maxx, int maxy)
 
     vt->cur_attrib = vt->default_attrib;
 
+    if (vt->cells) {
+        vt->cx = 0;
+        vt->cy = 0;
+        vt->cx_saved = 0;
+        vt->cy_saved = 0;
+        g_free(vt->cells);
+    }
     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;
     }
+   
+    if (vt->altcells) {
+        g_free(vt->altcells);
+    }
 
     vt->altcells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->height);
+}
 
+static void
+spiceterm_resize(spiceTerm *vt, uint32_t width, uint32_t height)
+{
+    width = (width/8)*8;
+    height = (height/16)*16;
+    
+    if (vt->screen->width == width && vt->screen->height == height) {
+        return;
+    }
+
+    DPRINTF(0, "width=%u height=%u", width, height);
+
+    spice_screen_resize(vt->screen, width, height);
+
+    init_spiceterm(vt, width, height);
+
+    struct winsize dimensions;
+    dimensions.ws_col = vt->width;
+    dimensions.ws_row = vt->height;
+
+    ioctl(vt->pty, TIOCSWINSZ, &dimensions);
+}
+
+static spiceTerm *
+create_spiceterm(int argc, char** argv, uint32_t maxx, uint32_t maxy, guint timeout)
+{
+    SpiceCoreInterface *core = basic_event_loop_init();
+    SpiceScreen *spice_screen = spice_screen_new(core, maxx, maxy, timeout);
+
+    //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
+    
+    spice_screen->image_cache = g_hash_table_new(g_int_hash, g_int_equal);
+
+    spiceTerm *vt = (spiceTerm *)calloc (sizeof(spiceTerm), 1);
+
+    vt->keyboard_sin.base.sif = &my_keyboard_sif.base;
+    spice_server_add_interface(spice_screen->server, &vt->keyboard_sin.base);
+
+    vt->vdagent_sin.base.sif = &my_vdagent_sif.base;
+    vt->vdagent_sin.subtype = "vdagent";
+    spice_server_add_interface(spice_screen->server, &vt->vdagent_sin.base);
     vt->screen = spice_screen;
 
+    init_spiceterm(vt, maxx, maxy);
+
     return vt;
 }
 
-static void master_watch(int master, int event, void *opaque)
+static gboolean
+master_error_callback(GIOChannel *channel, GIOCondition condition,
+                      gpointer data)
+{
+    //spiceTerm *vt = (spiceTerm *)data;
+
+    DPRINTF(1, "condition %d", condition);
+
+    exit(0);
+
+    return FALSE;
+}
+
+static void
+master_watch(int master, int event, void *opaque)
 {
     spiceTerm *vt = (spiceTerm *)opaque;
+    int c;
 
     // 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
+            perror("master pipe read error"); // fixme
         }
         spiceterm_puts (vt, buffer, c);
     } else {
         if (vt->ibuf_count > 0) {
-            DPRINTF(1, "%s: write input %x %d", __func__, vt->ibuf[0], vt->ibuf_count);
-            write (master, vt->ibuf, vt->ibuf_count);
-            vt->ibuf_count = 0; // fixme: what if not all data written
+            DPRINTF(1, "write input %x %d", vt->ibuf[0], vt->ibuf_count);
+            if ((c = write (master, vt->ibuf, vt->ibuf_count)) >= 0) {
+                if (c == vt->ibuf_count) {
+                    vt->ibuf_count = 0;
+                } else if (c > 0) {
+                    // not all data written
+                    memmove(vt->ibuf, vt->ibuf + c, vt->ibuf_count - c);
+                    vt->ibuf_count -= c;
+                } else {
+                    // nothing written -ignore and try later
+                }
+            } else {
+                perror("master pipe write error");
+            }
+        }
+        if (vt->ibuf_count == 0) {
+            spiceterm_update_watch_mask(vt, FALSE);
         }
-        vt->screen->core->watch_update_mask(vt->screen->mwatch, SPICE_WATCH_EVENT_READ);
     }
 }
 
@@ -1725,7 +2201,7 @@ main (int argc, char** argv)
 
     if (0) print_usage(NULL); // fixme:
 
-    spiceTerm *vt = create_spiceterm (argc, argv, 745, 400);
+    spiceTerm *vt = create_spiceterm (argc, argv, 744, 400, 10);
 
     setlocale(LC_ALL, ""); // set from environment
 
@@ -1739,9 +2215,9 @@ main (int argc, char** argv)
     dimensions.ws_col = vt->width;
     dimensions.ws_row = vt->height;
 
-    setenv ("TERM", TERM, 1);
+    setenv("TERM", TERM, 1);
 
-    DPRINTF(1, "%s: execute %s", __func__, command);
+    DPRINTF(1, "execute %s", command);
 
     pid = forkpty (&master, ptyname, NULL, &dimensions);
     if(!pid) {
@@ -1763,6 +2239,14 @@ main (int argc, char** argv)
         exit (-1);
     }
 
+    vt->pty = master;
+
+    /* watch for errors - we need to use glib directly because spice
+     * does not have SPICE_WATCH_EVENT for this */
+    GIOChannel *channel = g_io_channel_unix_new(master);
+    g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL);
+    g_io_channel_set_encoding(channel, NULL, NULL);
+    g_io_add_watch(channel, G_IO_ERR|G_IO_HUP, master_error_callback, vt);
 
     vt->screen->mwatch = vt->screen->core->watch_add(
         master, SPICE_WATCH_EVENT_READ /* |SPICE_WATCH_EVENT_WRITE */,
@@ -1770,17 +2254,6 @@ main (int argc, char** argv)
 
     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);