]> git.proxmox.com Git - spiceterm.git/blobdiff - spiceterm.c
bump version to 3.0-1
[spiceterm.git] / spiceterm.c
index ed4101609d4c4308433800be71c60113e4abb6c8..bcb38c6593c0105cee5ded71b10a9514085bbc9f 100644 (file)
@@ -41,6 +41,7 @@
 #include <sys/wait.h>
 #include <signal.h>
 #include <locale.h>
+#include <getopt.h>
 
 #include "spiceterm.h"
 
@@ -50,8 +51,6 @@
 #include <spice/macros.h>
 #include <spice/qxl_dev.h>
 
-#include <gdk/gdkkeysyms.h>
-
 #include "event_loop.h"
 #include "translations.h"
 
@@ -67,28 +66,11 @@ static int debug = 0;
 
 #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 */
 
 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
-print_usage(const char *msg)
-{
-    if (msg) { fprintf(stderr, "ERROR: %s\n", msg); }
-    fprintf(stderr, "USAGE: spiceterm [spiceopts] [-c command [args]]\n");
-}
-
 static void
 draw_char_at(spiceTerm *vt, int x, int y, gunichar2 ch, TextAttributes attrib)
 {
@@ -182,7 +164,7 @@ spiceterm_show_cursor(spiceTerm *vt, int show)
     }
 }
 
-static void
+void
 spiceterm_refresh(spiceTerm *vt)
 {
     int x, y, y1;
@@ -201,18 +183,48 @@ spiceterm_refresh(spiceTerm *vt)
     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 i;
+    int x, y, y1;
 
-    for (i = 0; i < vt->width*vt->total_height; i++) {
-        if (vt->cells[i].attrib.selected) {
-            vt->cells[i].attrib.selected = 0;
+    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;
     }
-
-    spiceterm_refresh(vt);
 }
 
 static void
@@ -298,7 +310,7 @@ spiceterm_scroll_up(spiceTerm *vt, int top, int bottom, int lines, int moveattr)
     }
 }
 
-static void
+void
 spiceterm_virtual_scroll(spiceTerm *vt, int lines)
 {
     if (vt->altbuf || lines == 0) return;
@@ -330,7 +342,7 @@ spiceterm_virtual_scroll(spiceTerm *vt, int lines)
     spiceterm_refresh(vt);
 }
 
-static void
+void
 spiceterm_respond_esc(spiceTerm *vt, const char *esc)
 {
     int len = strlen(esc);
@@ -341,6 +353,24 @@ spiceterm_respond_esc(spiceTerm *vt, const char *esc)
         for (i = 0; i < len; i++) {
             vt->ibuf[vt->ibuf_count++] = esc[i];
         }
+    } else {
+        fprintf(stderr, "input buffer oferflow\n");
+        return;
+    }
+}
+
+void
+spiceterm_respond_data(spiceTerm *vt, int len, uint8_t *data)
+{
+    int i;
+
+    if (vt->ibuf_count < (IBUFSIZE - len)) {
+        for (i = 0; i < len; i++) {
+            vt->ibuf[vt->ibuf_count++] = data[i];
+        }
+    } else {
+        fprintf(stderr, "input buffer oferflow\n");
+        return;
     }
 }
 
@@ -537,7 +567,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);
             }
         }
 
@@ -912,11 +942,7 @@ spiceterm_putchar(spiceTerm *vt, gunichar2 ch)
                 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;
@@ -1206,7 +1232,11 @@ spiceterm_puts(spiceTerm *vt, const char *buf, int len)
                     vt->utf_char = (vt->utf_char << 6) | (c & 0x3f);
                     vt->utf_count--;
                     if (vt->utf_count == 0) {
-                        tc = vt->utf_char;
+                       if (vt->utf_char <= G_MAXUINT16) {
+                           tc = vt->utf_char;
+                       } else {
+                           tc = 0;
+                       }
                     } else {
                         continue;
                     }
@@ -1255,24 +1285,7 @@ spiceterm_puts(spiceTerm *vt, const char *buf, int len)
     return len;
 }
 
-/* fixme:
 void
-spiceterm_set_xcut_text(char* str, int len, struct _rfbClientRec* cl)
-{
-  spiceTerm *vt =(spiceTerm *)cl->screen->screenData;
-
-  // seems str is Latin-1 encoded
-  if (vt->selection) free (vt->selection);
-  vt->selection = (gunichar2 *)malloc (len*sizeof (gunichar2));
-  int i;
-  for (i = 0; i < len; i++) {
-    vt->selection[i] = str[i] & 0xff;
-  }
-  vt->selection_len = len;
-}
-*/
-
-static void
 spiceterm_update_watch_mask(spiceTerm *vt, gboolean writable)
 {
     g_assert(vt != NULL);
@@ -1307,25 +1320,27 @@ spiceterm_respond_unichar2(spiceTerm *vt, gunichar2 uc)
         gint len = g_unichar_to_utf8(uc, buf);
 
         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");
-            }
+            spiceterm_respond_data(vt, len, (uint8_t *)buf);           
         }
     } else {
-        if ((vt->ibuf_count + 1) < IBUFSIZE) {
-            vt->ibuf[vt->ibuf_count++] = (char)uc;
-        } else {
-            fprintf(stderr, "warning: input buffer overflow\n");
-        }
+        uint8_t buf[1] = { (uint8_t)uc };
+        spiceterm_respond_data(vt, 1, buf);           
     }
 }
 
-static void
+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);
+}
+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);
@@ -1362,22 +1377,23 @@ spiceterm_motion_event(spiceTerm *vt, uint32_t x, uint32_t y, uint32_t buttons)
 
     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);
+            if (vdagent_owns_clipboard(vt)) {
+                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);
+            } 
         }
 
         button2_released = 0;
@@ -1449,461 +1465,19 @@ spiceterm_motion_event(spiceTerm *vt, uint32_t x, uint32_t y, uint32_t buttons)
         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)
-{
-    // 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)
-{
-    spiceTerm *vt = SPICE_CONTAINEROF(sin, spiceTerm, keyboard_sin);
-    static int control = 0;
-    static int shift = 0;
-    char *esc = NULL;
-
-    gunichar2 uc = 0;
-
-    DPRINTF(1, "flags=%d keySym=%08x", flags, keySym);
-
-    if (flags & 1) {
-        if (keySym == GDK_KEY_Shift_L || keySym == GDK_KEY_Shift_R) {
-            shift = 1;
-        } if (keySym == GDK_KEY_Control_L || keySym == GDK_KEY_Control_R) {
-            control = 1;
-        } else if (vt->ibuf_count < (IBUFSIZE - 32)) {
-
-            if (control) {
-                if(keySym >= 'a' && keySym <= 'z')
-                    uc = keySym - 'a' + 1;
-                else if (keySym >= 'A' && keySym <= 'Z')
-                    uc = keySym - 'A' + 1;
-                else
-                    uc = 0;
-
-            } else {
-                switch (keySym) {
-                case GDK_KEY_Escape:
-                    uc = 27; break;
-                case GDK_KEY_Return:
-                    uc = '\r'; break;
-                case GDK_KEY_BackSpace:
-                    uc = 8; break;
-                case GDK_KEY_Tab:
-                    uc = '\t'; break;
-                case GDK_KEY_Delete: /* kdch1 */
-                case GDK_KEY_KP_Delete:
-                    esc = "[3~";break;
-                case GDK_KEY_Home: /* khome */
-                case GDK_KEY_KP_Home:
-                    esc = "OH";break;
-                case GDK_KEY_End:
-                case GDK_KEY_KP_End: /* kend */
-                    esc = "OF";break;
-                case GDK_KEY_Insert: /* kich1 */
-                case GDK_KEY_KP_Insert:
-                    esc = "[2~";break;
-                case GDK_KEY_Up:
-                case GDK_KEY_KP_Up:  /* kcuu1 */
-                    esc = "OA";break;
-                case GDK_KEY_Down: /* kcud1 */
-                case GDK_KEY_KP_Down:
-                    esc = "OB";break;
-                case GDK_KEY_Right:
-                case GDK_KEY_KP_Right: /* kcuf1 */
-                    esc = "OC";break;
-                case GDK_KEY_Left:
-                case GDK_KEY_KP_Left: /* kcub1 */
-                    esc = "OD";break;
-                case GDK_KEY_Page_Up:
-                    if (shift) {
-                        spiceterm_virtual_scroll (vt, -vt->height/2);
-                        goto ret;
-                    }
-                    esc = "[5~";break;
-                case GDK_KEY_Page_Down:
-                    if (shift) {
-                        spiceterm_virtual_scroll (vt, vt->height/2);
-                        goto ret;
-                    }
-                    esc = "[6~";break;
-                case GDK_KEY_F1:
-                    esc = "OP";break;
-                case GDK_KEY_F2:
-                    esc = "OQ";break;
-                case GDK_KEY_F3:
-                    esc = "OR";break;
-                case GDK_KEY_F4:
-                    esc = "OS";break;
-                case GDK_KEY_F5:
-                    esc = "[15~";break;
-                case GDK_KEY_F6:
-                    esc = "[17~";break;
-                case GDK_KEY_F7:
-                    esc = "[18~";break;
-                case GDK_KEY_F8:
-                    esc = "[19~";break;
-                case GDK_KEY_F9:
-                    esc = "[20~";break;
-                case GDK_KEY_F10:
-                    esc = "[21~";break;
-                case GDK_KEY_F11:
-                    esc = "[23~";break;
-                case GDK_KEY_F12:
-                    esc = "[24~";break;
-                default:
-                    if (keySym < 0x100) {
-                        uc = keySym;
-                    }
-                    break;
-                }
-            }
-
-            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);
-            }
-
-            if (esc) {
-                spiceterm_respond_esc(vt, esc);
-            } else if (uc > 0) {
-                spiceterm_respond_unichar2(vt, uc);
-            }
-        }
     }
-
-
-ret:
-
-    if (flags & 2) { // 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) {
-            control = 0;
-        }
-    }
-
-    spiceterm_update_watch_mask(vt, TRUE);
-}
-
-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_keyval        = my_kbd_push_keyval,
-    .push_scan_freg     = my_kbd_push_key,
-    .get_leds           = my_kbd_get_leds,
-};
-
-
-/* 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 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);
-    
-    spice_server_char_device_wakeup(&vt->vdagent_sin);
-
-    free(caps);
-}
-
-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_grab_clipboard(spiceTerm *vt)
-{
-    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;
-
-    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];
-    VDAgentClipboardGrab *grab = (VDAgentClipboardGrab *)&msg[1];
-    *((uint32_t *)grab) = 0;
-    *((uint8_t *)grab) = selection;
-
-    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)
-{
-    uint32_t size;
-    
-    DPRINTF(0, "REQUEST");
-
-    uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_PRIMARY; // fixme?
-
-    agent_owns_clipboard[selection] = 1;
-
-    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 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(0, "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));
-        }
-
-        vdagent_send_capabilities(vt, 0);
-        break;
-    }
-    case VD_AGENT_CLIPBOARD_GRAB: {
-        VDAgentClipboardGrab *grab = (VDAgentClipboardGrab *)&msg[1];
-        uint8_t selection = *((uint8_t *)grab);
-        DPRINTF(0, "VD_AGENT_CLIPBOARD_GRAB %d", selection);
-        agent_owns_clipboard[selection] = 0;
-        break;
-    }
-    case VD_AGENT_CLIPBOARD_REQUEST: {
-        DPRINTF(0, "VD_AGENT_CLIPBOARD_REQUEST");
-         
-        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(0, "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: {
-        DPRINTF(0, "VD_AGENT_CLIPBOARD_RELEASE");
-         
-        break;
-    }
-    case VD_AGENT_MONITORS_CONFIG:
-        /* ignore for now */
-        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(0, "%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) {
-        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);
-    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 spiceTerm *
-create_spiceterm(int argc, char** argv, int maxx, int maxy, guint timeout)
+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,18 +1503,49 @@ 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);
+}
+
+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);
 
-    vt->screen = spice_screen;
+    spice_screen_resize(vt->screen, width, height);
 
-    return vt;
+    init_spiceterm(vt, width, height);
+
+    struct winsize dimensions;
+    dimensions.ws_col = vt->width;
+    dimensions.ws_row = vt->height;
+
+    ioctl(vt->pty, TIOCSWINSZ, &dimensions);
 }
 
 static gboolean
@@ -1996,32 +1601,98 @@ master_watch(int master, int event, void *opaque)
     }
 }
 
+static void
+spiceterm_print_usage(const char *msg)
+{
+    if (msg) { 
+        fprintf(stderr, "ERROR: %s\n", msg); 
+    }
+    fprintf(stderr, "USAGE: spiceterm [OPTIONS] [-- command [args]]\n");
+    fprintf(stderr, "  --timeout <seconds>  Wait this time before aborting (default is 10 seconds)\n");
+    fprintf(stderr, "  --authpath <path>    Authentication path  (PVE AUTH)\n");
+    fprintf(stderr, "  --permission <perm>  Required permissions (PVE AUTH)\n");
+    fprintf(stderr, "  --port <port>        Bind to port <port>\n");
+    fprintf(stderr, "  --addr <addr>        Bind to address <addr>\n");
+    fprintf(stderr, "  --sasl               Enable SASL based authentication\n");
+    fprintf(stderr, "  --noauth             Disable authentication\n");
+    fprintf(stderr, "  --keymap             Spefify keymap (uses kvm keymap files)\n");
+}
+
 int
 main (int argc, char** argv)
 {
-    int i;
+    int c;
     char **cmdargv = NULL;
     char *command = "/bin/bash"; // execute normal shell as default
     int pid;
     int master;
     char ptyname[1024];
     struct winsize dimensions;
-
-    g_thread_init(NULL);
-
-    for (i = 1; i < argc; i++) {
-        if (!strcmp (argv[i], "-c")) {
-            command = argv[i+1];
-            cmdargv = &argv[i+1];
-            argc = i;
-            argv[i] = NULL;
+    SpiceTermOptions opts = {
+        .timeout = 10,
+        .port = 5900,
+        .addr = NULL,
+        .noauth = FALSE,
+        .sasl = FALSE,
+    };
+
+   static struct option long_options[] = {
+        { "timeout", required_argument, 0,  't' },
+        { "authpath", required_argument, 0, 'A' },
+        { "permissions", required_argument, 0, 'P' },
+        { "port", required_argument, 0, 'p' },
+        { "addr", required_argument, 0, 'a' },
+        { "keymap", required_argument, 0, 'k' },
+        { "noauth", no_argument, 0, 'n' },
+        { "sasl", no_argument, 0, 's' },
+        { NULL, 0, 0, 0 },
+    };
+
+    while ((c = getopt_long(argc, argv, "nkst:a:p:P:", long_options, NULL)) != -1) {
+        
+        switch (c) {
+        case 'n':
+            opts.noauth = TRUE;
             break;
+        case 's':
+            opts.sasl = TRUE;
+            break;
+        case 'k':
+            opts.keymap = optarg;
+            break;
+        case 'A':
+            pve_auth_set_path(optarg);
+            break;
+        case 'P':
+            pve_auth_set_permissions(optarg);
+            break;
+        case 'p':
+            opts.port = atoi(optarg);
+            break;
+         case 'a':
+            opts.addr = optarg;
+            break;
+        case 't':
+            opts.timeout = atoi(optarg);
+            break;
+        case '?':
+            spiceterm_print_usage(NULL);
+            exit(-1);
+            break;
+        default:
+            spiceterm_print_usage("getopt returned unknown character code");
+            exit(-1);            
         }
     }
+    if (optind < argc) {
+        command = argv[optind];
+        cmdargv = &argv[optind];
+    }
 
-    if (0) print_usage(NULL); // fixme:
-
-    spiceTerm *vt = create_spiceterm (argc, argv, 745, 400, 10);
+    spiceTerm *vt = spiceterm_create(744, 400, &opts);
+    if (!vt)
+        exit(-1);
 
     setlocale(LC_ALL, ""); // set from environment
 
@@ -2059,6 +1730,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);