]> git.proxmox.com Git - spiceterm.git/blobdiff - spiceterm.c
use correct raster on resize
[spiceterm.git] / spiceterm.c
index ed4101609d4c4308433800be71c60113e4abb6c8..dd63b90e0a6c95b21d1f3add8646ca191e0e0731 100644 (file)
@@ -79,8 +79,10 @@ unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
                                8,12,10,14, 9,13,11,15 };
 
 
-static void vdagent_grab_clipboard(spiceTerm *vt);
-static void vdagent_request_clipboard(spiceTerm *vt);
+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)
@@ -537,7 +539,7 @@ 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);
             }
         }
 
@@ -1325,135 +1327,6 @@ spiceterm_respond_unichar2(spiceTerm *vt, gunichar2 uc)
     }
 }
 
-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);
-        }
-    }
-
-    if (buttons & 4) {
-
-
-        if(button2_released) {
-
-            if (1) { // fixme:
-                vdagent_request_clipboard(vt);
-            } else 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);
-                }
-            }
-        }
-
-        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;
-
-        if (vt->selection) free (vt->selection);
-        vt->selection = (gunichar2 *)malloc (len*sizeof(gunichar2));
-        vt->selection_len = len;
-
-        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);
-        // fixme: tell client we have something seletced
-        //rfbGotXCutText (vt->screen, sel_latin1, len);
-    }
-}
-
 static void
 my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
 {
@@ -1465,9 +1338,8 @@ my_kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
 }
 
 static void
-my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags)
+spiceterm_push_keyval(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;
@@ -1476,7 +1348,7 @@ my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags)
 
     DPRINTF(1, "flags=%d keySym=%08x", flags, keySym);
 
-    if (flags & 1) {
+    if (flags & VD_AGENT_KEYVAL_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) {
@@ -1573,7 +1445,7 @@ my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags)
 
             if (vt->y_displ != vt->y_base) {
                 vt->y_displ = vt->y_base;
-                spiceterm_refresh (vt);
+                spiceterm_refresh(vt);
             }
 
             if (esc) {
@@ -1584,10 +1456,9 @@ my_kbd_push_keyval(SpiceKbdInstance *sin, uint32_t keySym, int flags)
         }
     }
 
-
 ret:
 
-    if (flags & 2) { // UP
+    if (!(flags & VD_AGENT_KEYVAL_FLAG_DOWN)) { // 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) {
@@ -1609,7 +1480,6 @@ static SpiceKbdInterface my_keyboard_sif = {
     .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,
 };
@@ -1622,6 +1492,178 @@ 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_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);
+        }
+    }
+
+    if (buttons & 4) {
+
+        if(button2_released) {
+
+            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);
+            } 
+        }
+
+        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;
+
+        if (vt->selection) free (vt->selection);
+        vt->selection = (gunichar2 *)malloc (len*sizeof(gunichar2));
+        vt->selection_len = len;
+
+        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;
+
+    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;
@@ -1639,6 +1681,7 @@ static void vdagent_send_capabilities(spiceTerm *vt, uint32_t request)
     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);
+    VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_KEYVAL);
 
     int msg_size =  sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
     g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE);
@@ -1657,35 +1700,21 @@ static void vdagent_send_capabilities(spiceTerm *vt, uint32_t request)
     msg->size = size;
 
     memcpy(buf + sizeof(VDIChunkHeader) + sizeof(VDAgentMessage), (uint8_t *)caps, size);
-    
-    spice_server_char_device_wakeup(&vt->vdagent_sin);
-
-    free(caps);
-}
 
-static void
-dump_message(unsigned char *buf, int size)
-{
-    int i;
+    if (0) dump_message(buf, msg_size);
 
-    for (i = 0; i < size; i++) {
-        printf("%d  %02X\n", i, buf[i]);
-    }
+    spice_server_char_device_wakeup(&vt->vdagent_sin);
 
-    exit(0);
+    free(caps);
 }
 
-static void vdagent_grab_clipboard(spiceTerm *vt)
+static void vdagent_grab_clipboard(spiceTerm *vt, uint8_t selection)
 {
     uint32_t size;
     
-    DPRINTF(0, "GRAB");
-
-    uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_PRIMARY; // fixme?
-
     agent_owns_clipboard[selection] = 1;
 
-    size = 4; // fixme: HACK, because sizeof(VDAgentClipboardGrab) == 0;
+    size = 8;
 
     int msg_size =  sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) + size;
     g_assert((vdagent_write_buffer_pos + msg_size) < VDAGENT_WBUF_SIZE);
@@ -1697,9 +1726,9 @@ static void vdagent_grab_clipboard(spiceTerm *vt)
 
     VDIChunkHeader *hdr = (VDIChunkHeader *)buf;
     VDAgentMessage *msg = (VDAgentMessage *)&hdr[1];
-    VDAgentClipboardGrab *grab = (VDAgentClipboardGrab *)&msg[1];
-    *((uint32_t *)grab) = 0;
+    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;
@@ -1711,18 +1740,13 @@ static void vdagent_grab_clipboard(spiceTerm *vt)
 
     if (0) dump_message(buf, msg_size);
 
-    
     spice_server_char_device_wakeup(&vt->vdagent_sin);
 }
 
-static void vdagent_request_clipboard(spiceTerm *vt)
+static void vdagent_request_clipboard(spiceTerm *vt, uint8_t selection)
 {
     uint32_t size;
     
-    DPRINTF(0, "REQUEST");
-
-    uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_PRIMARY; // fixme?
-
     agent_owns_clipboard[selection] = 1;
 
     size = 4 + sizeof(VDAgentClipboardRequest);
@@ -1752,7 +1776,59 @@ static void vdagent_request_clipboard(spiceTerm *vt)
 
     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);
 }
 
@@ -1770,6 +1846,11 @@ vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
     DPRINTF(1, "%d %d %d %d", len, hdr->port, msg->protocol, msg->type);
 
     switch (msg->type) {
+    case VD_AGENT_KEYVAL: {
+        VDAgentKeyval *info = (VDAgentKeyval *)&msg[1];
+        spiceterm_push_keyval(vt, info->keyval, info->flags);
+        break;
+    } 
     case VD_AGENT_MOUSE_STATE: { 
         VDAgentMouseState *info = (VDAgentMouseState *)&msg[1];
         spiceterm_motion_event(vt, info->x, info->y, info->buttons);
@@ -1777,12 +1858,12 @@ vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
     }
     case VD_AGENT_ANNOUNCE_CAPABILITIES: {
         VDAgentAnnounceCapabilities *caps = (VDAgentAnnounceCapabilities *)&msg[1];
-        DPRINTF(0, "VD_AGENT_ANNOUNCE_CAPABILITIES %d", caps->request);
+        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(0, "CAPABILITIES %d %d", i, VD_AGENT_HAS_CAPABILITY(caps->caps, caps_size, i));
+            DPRINTF(1, "CAPABILITIES %d %d", i, VD_AGENT_HAS_CAPABILITY(caps->caps, caps_size, i));
         }
 
         vdagent_send_capabilities(vt, 0);
@@ -1791,13 +1872,19 @@ vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
     case VD_AGENT_CLIPBOARD_GRAB: {
         VDAgentClipboardGrab *grab = (VDAgentClipboardGrab *)&msg[1];
         uint8_t selection = *((uint8_t *)grab);
-        DPRINTF(0, "VD_AGENT_CLIPBOARD_GRAB %d", selection);
+        DPRINTF(1, "VD_AGENT_CLIPBOARD_GRAB %d", selection);
         agent_owns_clipboard[selection] = 0;
         break;
     }
     case VD_AGENT_CLIPBOARD_REQUEST: {
-        DPRINTF(0, "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: {
@@ -1805,7 +1892,7 @@ vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
         uint8_t selection = data[0];
         uint32_t type = *(uint32_t *)(data + 4);
         int size = msg->size - 8;
-        DPRINTF(0, "VD_AGENT_CLIPBOARD %d %d %d", selection, type, size);
+        DPRINTF(1, "VD_AGENT_CLIPBOARD %d %d %d", selection, type, size);
 
         if (type == VD_AGENT_CLIPBOARD_UTF8_TEXT) {
             int i;
@@ -1821,13 +1908,24 @@ vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
         break;
     }
     case VD_AGENT_CLIPBOARD_RELEASE: {
-        DPRINTF(0, "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:
-        /* ignore for now */
+    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);
     }
@@ -1838,7 +1936,7 @@ vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
 static int
 vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
 {
-    DPRINTF(0, "%d %d", len,  vdagent_write_buffer_pos);
+    DPRINTF(1, "%d %d", len,  vdagent_write_buffer_pos);
     g_assert(len >= 8);
 
     if (!vdagent_write_buffer_pos) {
@@ -1848,13 +1946,12 @@ vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
     int size = (len >= vdagent_write_buffer_pos) ? vdagent_write_buffer_pos : len;
     memcpy(buf, vdagent_write_buffer, size);
     if (size < vdagent_write_buffer_pos) {
-        DPRINTF(0, "MOVE %d", size);
         memmove(vdagent_write_buffer, vdagent_write_buffer + size, 
                 vdagent_write_buffer_pos - size);
     }
     vdagent_write_buffer_pos -= size;
 
-    DPRINTF(0, "RET %d %d", size,  vdagent_write_buffer_pos);
+    DPRINTF(1, "RET %d %d", size,  vdagent_write_buffer_pos);
     return size;
 }
 
@@ -1874,36 +1971,16 @@ static SpiceCharDeviceInterface my_vdagent_sif = {
     .read               = vmc_read,
 };
 
-static spiceTerm *
-create_spiceterm(int argc, char** argv, int maxx, int maxy, guint timeout)
+static void
+init_spiceterm(spiceTerm *vt, uint32_t width, uint32_t height)
 {
     int i;
 
-    SpiceScreen *spice_screen;
-
-    SpiceCoreInterface *core = basic_event_loop_init();
-    spice_screen = spice_screen_new(core, timeout);
-    //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
-
-    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);
-
-    // screen->setXCutText = spiceterm_set_xcut_text;
-    // screen->ptrAddEvent = spiceterm_pointer_event;
-    // screen->newClientHook = new_client;
-    // screen->desktopName = "SPICE Command Terminal";
-
-    vt->maxx = spice_screen->width;
-    vt->maxy = spice_screen->height;
+    g_assert(vt != NULL);
+    g_assert(vt->screen != NULL);
 
-    vt->width = vt->maxx / 8;
-    vt->height = vt->maxy / 16;
+    vt->width = width / 8;
+    vt->height = height / 16;
 
     vt->total_height = vt->height * 20;
     vt->scroll_height = 0;
@@ -1929,17 +2006,73 @@ create_spiceterm(int argc, char** argv, int maxx, int maxy, guint timeout)
 
     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;
 }
 
@@ -2021,7 +2154,7 @@ main (int argc, char** argv)
 
     if (0) print_usage(NULL); // fixme:
 
-    spiceTerm *vt = create_spiceterm (argc, argv, 745, 400, 10);
+    spiceTerm *vt = create_spiceterm (argc, argv, 744, 400, 10);
 
     setlocale(LC_ALL, ""); // set from environment
 
@@ -2059,6 +2192,8 @@ 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);