]> git.proxmox.com Git - spiceterm.git/blobdiff - screen.c
updates for debian jessie
[spiceterm.git] / screen.c
index 231fbbe5ec9f6d2db821755d0c28b352943231ae..6f253d2047042832b6563bc23b9781c226380ebd 100644 (file)
--- a/screen.c
+++ b/screen.c
@@ -39,6 +39,8 @@
 #include <spice/enums.h>
 #include <spice/macros.h>
 #include <spice/qxl_dev.h>
+#include <spice/vd_agent.h>
+#include <sasl/sasl.h>
 
 #include "glyphs.h"
 
@@ -54,9 +56,6 @@ static int debug = 0;
 
 #define MEM_SLOT_GROUP_ID 0
 
-#define NOTIFY_DISPLAY_BATCH (SINGLE_PART/2)
-#define NOTIFY_CURSOR_BATCH 10
-
 /* these colours are from linux kernel drivers/char/vt.c */
 /* the default colour table, for VGA+ colour systems */
 int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa,
@@ -69,10 +68,11 @@ int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa,
 /* Parts cribbed from spice-display.h/.c/qxl.c */
 
 typedef struct SimpleSpiceUpdate {
-    QXLCommandExt ext; // first
+    QXLCommandExt ext; // needs to be first member
     QXLDrawable drawable;
     QXLImage image;
     uint8_t *bitmap;
+    int cache_id; // do not free bitmap if cache_id != 0
 } SimpleSpiceUpdate;
 
 static void 
@@ -85,12 +85,46 @@ spice_screen_destroy_update(SimpleSpiceUpdate *update)
         uint8_t *ptr = (uint8_t*)update->drawable.clip.data;
         free(ptr);
     }
-    g_free(update->bitmap);
+    if (update->bitmap && !update->cache_id) {
+        g_free(update->bitmap);
+    }
+
     g_free(update);
 }
 
-#define DEFAULT_WIDTH 640
-#define DEFAULT_HEIGHT 320
+static void 
+release_qxl_command_ext(QXLCommandExt *ext)
+{
+    g_assert(ext != NULL);
+
+    switch (ext->cmd.type) {
+    case QXL_CMD_DRAW:
+        spice_screen_destroy_update((void*)ext);
+        break;
+    case QXL_CMD_SURFACE:
+        free(ext);
+        break;
+    case QXL_CMD_CURSOR: {
+        QXLCursorCmd *cmd = (QXLCursorCmd *)(unsigned long)ext->cmd.data;
+        if (cmd->type == QXL_CURSOR_SET) {
+            free(cmd);
+        }
+        free(ext);
+        break;
+    }
+    default:
+        abort();
+    }
+}
+
+static void 
+release_resource(QXLInstance *qin, struct QXLReleaseInfoExt release_info)
+{
+    QXLCommandExt *ext = (QXLCommandExt*)(unsigned long)release_info.info->id;
+
+    g_assert(release_info.group_id == MEM_SLOT_GROUP_ID);
+    release_qxl_command_ext(ext);
+}
 
 static int unique = 0x0ffff + 1;
 
@@ -116,20 +150,30 @@ simple_set_release_info(QXLReleaseInfo *info, intptr_t ptr)
 static void 
 push_command(SpiceScreen *spice_screen, QXLCommandExt *ext)
 {
-    g_mutex_lock(spice_screen->command_mutex);
+    int need_wakeup = 1;
+
+    g_mutex_lock(&spice_screen->command_mutex);
 
     while (spice_screen->commands_end - spice_screen->commands_start >= COMMANDS_SIZE) {
-        g_cond_wait(spice_screen->command_cond, spice_screen->command_mutex);
+        g_cond_wait(&spice_screen->command_cond, &spice_screen->command_mutex);
     }
 
     g_assert(spice_screen->commands_end - spice_screen->commands_start < COMMANDS_SIZE);
 
+    if ((spice_screen->commands_end - spice_screen->commands_start) > 0) {
+        need_wakeup = 0;
+    }
+
     spice_screen->commands[spice_screen->commands_end % COMMANDS_SIZE] = ext;
     spice_screen->commands_end++;
 
-    g_mutex_unlock(spice_screen->command_mutex);
+    if (need_wakeup) {
+       spice_qxl_wakeup(&spice_screen->qxl_instance);
+        //spice_screen->qxl_worker->wakeup(spice_screen->qxl_worker);
+    }
+
+    g_mutex_unlock(&spice_screen->command_mutex);
 
-    spice_screen->qxl_worker->wakeup(spice_screen->qxl_worker);
 }
 
 /* bitmap are freed, so they must be allocated with g_malloc */
@@ -167,6 +211,8 @@ spice_screen_update_from_bitmap_cmd(uint32_t surface_id, QXLRect bbox, uint8_t *
 
     if (cache_id) {
         QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, cache_id);
+        image->descriptor.flags = SPICE_IMAGE_FLAGS_CACHE_ME;
+        update->cache_id = cache_id;
     } else {
         QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ++unique);
     }
@@ -190,14 +236,18 @@ spice_screen_draw_char_cmd(SpiceScreen *spice_screen, int x, int y, int c,
 {
     int top, left;
     uint8_t *dst;
-    uint8_t *bitmap;
+    uint8_t *bitmap = NULL;
     int bw, bh;
     int i, j;
     QXLRect bbox;
     int cache_id = 0;
+    CachedImage *ce;
 
     if (!uline && c < 256) {
         cache_id = ((fg << 12) | (bg << 8) | (c & 255)) & 0x0ffff;
+        if ((ce = (CachedImage *)g_hash_table_lookup(spice_screen->image_cache, &cache_id))) {
+            bitmap = ce->bitmap;
+        }
     }
 
     left = x*8;
@@ -206,42 +256,48 @@ spice_screen_draw_char_cmd(SpiceScreen *spice_screen, int x, int y, int c,
     bw       = 8;
     bh       = 16;
 
-    bitmap = dst = g_malloc(bw * bh * 4);
-
-    unsigned char *data = vt_font_data + c*16;
-    unsigned char d = *data;
-
-    g_assert(fg >= 0 && fg < 16);
-    g_assert(bg >= 0 && bg < 16);
-
-    unsigned char fgc_red = default_red[fg];
-    unsigned char fgc_blue = default_blu[fg];
-    unsigned char fgc_green = default_grn[fg];
-    unsigned char bgc_red = default_red[bg];
-    unsigned char bgc_blue = default_blu[bg];
-    unsigned char bgc_green = default_grn[bg];
-
-    for (j = 0; j < 16; j++) {
-        gboolean ul = (j == 14) && uline;
-        for (i = 0; i < 8; i++) {
-            if (i == 0) {
-                d=*data;
-                data++;
+    if (!bitmap) {
+        bitmap = dst = g_malloc(bw * bh * 4);
+        
+        unsigned char *data = vt_font_data + c*16;
+        unsigned char d = *data;
+
+        g_assert(fg >= 0 && fg < 16);
+        g_assert(bg >= 0 && bg < 16);
+
+        unsigned char fgc_red = default_red[fg];
+        unsigned char fgc_blue = default_blu[fg];
+        unsigned char fgc_green = default_grn[fg];
+        unsigned char bgc_red = default_red[bg];
+        unsigned char bgc_blue = default_blu[bg];
+        unsigned char bgc_green = default_grn[bg];
+
+        for (j = 0; j < 16; j++) {
+            gboolean ul = (j == 14) && uline;
+            for (i = 0; i < 8; i++) {
+                if (i == 0) {
+                    d=*data;
+                    data++;
+                }
+                if (ul || d&0x80) {
+                    *(dst) = fgc_blue;
+                    *(dst+1) = fgc_green;
+                    *(dst+2) = fgc_red;
+                    *(dst+3) = 0;
+                } else {
+                    *(dst) = bgc_blue;
+                    *(dst+1) = bgc_green;
+                    *(dst+2) = bgc_red;
+                    *(dst+3) = 0;
+                }
+                d<<=1;
+                dst += 4;
             }
-            if (ul || d&0x80) {
-                 *(dst) = fgc_blue;
-                 *(dst+1) = fgc_green;
-                 *(dst+2) = fgc_red;
-                 *(dst+3) = 0;
-            } else {
-                 *(dst) = bgc_blue;
-                 *(dst+1) = bgc_green;
-                 *(dst+2) = bgc_red;
-                 *(dst+3) = 0;
-            }
-            d<<=1;
-            dst += 4;
         }
+        ce = g_new(CachedImage, 1);
+        ce->cache_id = cache_id;
+        ce->bitmap = bitmap;
+        g_hash_table_insert(spice_screen->image_cache, &ce->cache_id, ce);
     }
 
     bbox.left = left; bbox.top = top;
@@ -324,14 +380,17 @@ static void
 create_primary_surface(SpiceScreen *spice_screen, uint32_t width, 
                        uint32_t height)
 {
-    QXLWorker *qxl_worker = spice_screen->qxl_worker;
     QXLDevSurfaceCreate surface = { 0, };
 
-    g_assert(height <= MAX_HEIGHT);
-    g_assert(width <= MAX_WIDTH);
     g_assert(height > 0);
     g_assert(width > 0);
 
+    if (height > MAX_HEIGHT)
+        height = MAX_HEIGHT;
+
+    if (width > MAX_WIDTH)
+        width = MAX_WIDTH;
+
     surface.format     = SPICE_SURFACE_FMT_32_xRGB;
     surface.width      = spice_screen->primary_width = width;
     surface.height     = spice_screen->primary_height = height;
@@ -346,7 +405,9 @@ create_primary_surface(SpiceScreen *spice_screen, uint32_t width,
     spice_screen->width = width;
     spice_screen->height = height;
 
-    qxl_worker->create_primary_surface(qxl_worker, 0, &surface);
+    spice_screen->cursor_set = 0;
+
+    spice_qxl_create_primary_surface(&spice_screen->qxl_instance, 0, &surface);
 }
 
 QXLDevMemSlot slot = {
@@ -369,9 +430,9 @@ attache_worker(QXLInstance *qin, QXLWorker *_qxl_worker)
     }
  
     spice_screen->qxl_worker = _qxl_worker;
-    spice_screen->qxl_worker->add_memslot(spice_screen->qxl_worker, &slot);
-    create_primary_surface(spice_screen, DEFAULT_WIDTH, DEFAULT_HEIGHT);
-    spice_screen->qxl_worker->start(spice_screen->qxl_worker);
+    spice_qxl_add_memslot(&spice_screen->qxl_instance, &slot);
+    create_primary_surface(spice_screen, spice_screen->width, spice_screen->height);
+    spice_server_vm_start(spice_screen->server);
 }
 
 static void 
@@ -404,7 +465,7 @@ get_command(QXLInstance *qin, struct QXLCommandExt *ext)
     SpiceScreen *spice_screen = SPICE_CONTAINEROF(qin, SpiceScreen, qxl_instance);
     int res = FALSE;
 
-    g_mutex_lock(spice_screen->command_mutex);
+    g_mutex_lock(&spice_screen->command_mutex);
     
     if ((spice_screen->commands_end - spice_screen->commands_start) == 0) {
         res = FALSE;
@@ -414,15 +475,28 @@ get_command(QXLInstance *qin, struct QXLCommandExt *ext)
     *ext = *spice_screen->commands[spice_screen->commands_start % COMMANDS_SIZE];
     g_assert(spice_screen->commands_start < spice_screen->commands_end);
     spice_screen->commands_start++;
-    g_cond_signal(spice_screen->command_cond);
+    g_cond_signal(&spice_screen->command_cond);
 
     res = TRUE;
 
 ret:
-    g_mutex_unlock(spice_screen->command_mutex);
+    g_mutex_unlock(&spice_screen->command_mutex);
     return res;
 }
 
+void
+discard_pending_commands(SpiceScreen *spice_screen) 
+{
+    int pos;
+
+    g_mutex_lock(&spice_screen->command_mutex);
+    for (pos = spice_screen->commands_start; pos < spice_screen->commands_end; pos++) {
+        release_qxl_command_ext(spice_screen->commands[pos % COMMANDS_SIZE]);
+    }
+    spice_screen->commands_start = spice_screen->commands_end;
+    g_mutex_unlock(&spice_screen->command_mutex);
+}
+
 static int 
 req_cmd_notification(QXLInstance *qin)
 {
@@ -432,32 +506,6 @@ req_cmd_notification(QXLInstance *qin)
     return TRUE;
 }
 
-static void 
-release_resource(QXLInstance *qin, struct QXLReleaseInfoExt release_info)
-{
-    QXLCommandExt *ext = (QXLCommandExt*)(unsigned long)release_info.info->id;
-
-    g_assert(release_info.group_id == MEM_SLOT_GROUP_ID);
-    switch (ext->cmd.type) {
-        case QXL_CMD_DRAW:
-            spice_screen_destroy_update((void*)ext);
-            break;
-        case QXL_CMD_SURFACE:
-            free(ext);
-            break;
-        case QXL_CMD_CURSOR: {
-            QXLCursorCmd *cmd = (QXLCursorCmd *)(unsigned long)ext->cmd.data;
-            if (cmd->type == QXL_CURSOR_SET) {
-                free(cmd);
-            }
-            free(ext);
-            break;
-        }
-        default:
-            abort();
-    }
-}
-
 #define CURSOR_WIDTH 8
 #define CURSOR_HEIGHT 16
 
@@ -491,40 +539,32 @@ static int
 get_cursor_command(QXLInstance *qin, struct QXLCommandExt *ext)
 {
     SpiceScreen *spice_screen = SPICE_CONTAINEROF(qin, SpiceScreen, qxl_instance);
-    static int set = 1;
-    static int x = 0, y = 0;
     QXLCursorCmd *cursor_cmd;
     QXLCommandExt *cmd;
 
-    if (!spice_screen->cursor_notify) {
+    if (spice_screen->cursor_set) 
         return FALSE;
-    }
-
-    spice_screen->cursor_notify--;
+        
+    spice_screen->cursor_set = 1;
+    
     cmd = calloc(sizeof(QXLCommandExt), 1);
     cursor_cmd = calloc(sizeof(QXLCursorCmd), 1);
 
     cursor_cmd->release_info.id = (unsigned long)cmd;
 
-    if (set) {
-        cursor_cmd->type = QXL_CURSOR_SET;
-        cursor_cmd->u.set.position.x = 0;
-        cursor_cmd->u.set.position.y = 0;
-        cursor_cmd->u.set.visible = TRUE;
-        cursor_cmd->u.set.shape = (unsigned long)&cursor;
-        // white rect as cursor
-        memset(cursor.data, 255, sizeof(cursor.data));
-        set = 0;
-    } else {
-        cursor_cmd->type = QXL_CURSOR_MOVE;
-        cursor_cmd->u.position.x = x++ % spice_screen->primary_width;
-        cursor_cmd->u.position.y = y++ % spice_screen->primary_height;
-    }
+    cursor_cmd->type = QXL_CURSOR_SET;
+    cursor_cmd->u.set.position.x = 0;
+    cursor_cmd->u.set.position.y = 0;
+    cursor_cmd->u.set.visible = TRUE;
+    cursor_cmd->u.set.shape = (unsigned long)&cursor;
+    // white rect as cursor
+    memset(cursor.data, 255, sizeof(cursor.data));
 
     cmd->cmd.data = (unsigned long)cursor_cmd;
     cmd->cmd.type = QXL_CMD_CURSOR;
     cmd->group_id = MEM_SLOT_GROUP_ID;
-    cmd->flags    = 0;
+    cmd->flags = 0;
     *ext = *cmd;
 
     return TRUE;
@@ -607,6 +647,7 @@ do_conn_timeout(void *opaque)
     }
 }
 
+
 QXLInterface display_sif = {
     .base = {
         .type = SPICE_INTERFACE_QXL,
@@ -631,48 +672,21 @@ QXLInterface display_sif = {
     .set_client_capabilities = set_client_capabilities,
 };
 
-/* vdagent interface - not sure why we need that? */
-static int 
-vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
-{
-    return len;
-}
-
-static int 
-vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
-{
-    return 0;
-}
-
-static void 
-vmc_state(SpiceCharDeviceInstance *sin, int connected)
-{
-
-}
-
-static SpiceCharDeviceInterface 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,
-};
-
-SpiceCharDeviceInstance vdagent_sin = {
-    .base = {
-        .sif = &vdagent_sif.base,
-    },
-    .subtype = "vdagent",
-};
 
 void 
-spice_screen_draw_char(SpiceScreen *spice_screen, int x, int y, gunichar2 ch, TextAttributes attrib)
+spice_screen_draw_char(SpiceScreen *spice_screen, int x, int y, gunichar2 ch, 
+                       TextAttributes attrib)
 {
     int fg, bg;
 
+    int invers;
     if (attrib.invers) {
+        invers = attrib.selected ? 0 : 1;
+    } else {
+        invers = attrib.selected ? 1 : 0;
+    }        
+
+    if (invers) {
         bg = attrib.fgcol;
         fg = attrib.bgcol;
     } else {
@@ -693,15 +707,68 @@ spice_screen_draw_char(SpiceScreen *spice_screen, int x, int y, gunichar2 ch, Te
     push_command(spice_screen, &update->ext);
 }
 
+static int 
+sasl_checkpass_cb(sasl_conn_t *conn,
+                  void *context,
+                  const char *user,
+                  const char *pass,
+                  unsigned passlen,
+                  struct propctx *propctx)
+{
+    const void *remoteport = NULL;
+    char *clientip = NULL;
+    if (sasl_getprop(conn, SASL_IPREMOTEPORT, &remoteport) == SASL_OK) {
+        clientip = strtok(g_strdup(remoteport), ";");
+    } else {
+        clientip = g_strdup("unknown");
+    }
+
+    int res = pve_auth_verify(clientip, user, pass);
+
+    g_free(clientip);
+
+    return (res == 0) ? SASL_OK : SASL_NOAUTHZ;
+}
+
+static int 
+sasl_getopt_cb(void *context, const char *plugin_name,
+               const char *option,
+               const char **result, unsigned *len)
+{
+    if (strcmp(option, "mech_list") == 0) {
+        *result = "plain";
+        len = NULL;
+        return SASL_OK;
+    }
+
+    return SASL_FAIL;
+}
+
+typedef int sasl_cb_fn(void);  
+static sasl_callback_t sasl_callbacks[] = {
+    { SASL_CB_GETOPT, (sasl_cb_fn *)sasl_getopt_cb, NULL },
+    { SASL_CB_SERVER_USERDB_CHECKPASS, (sasl_cb_fn *)sasl_checkpass_cb, NULL },
+    { SASL_CB_LIST_END, NULL, NULL },
+};
 SpiceScreen *
-spice_screen_new(SpiceCoreInterface *core, guint timeout)
+spice_screen_new(SpiceCoreInterface *core, uint32_t width, uint32_t height, 
+                 SpiceTermOptions *opts)
 {
-    int port = 5912;
     SpiceScreen *spice_screen = g_new0(SpiceScreen, 1);
     SpiceServer* server = spice_server_new();
+    char *x509_key_file = "/etc/pve/local/pve-ssl.key";
+    char *x509_cert_file = "/etc/pve/local/pve-ssl.pem";
+    char *x509_cacert_file = "/etc/pve/pve-root-ca.pem";
+    char *x509_key_password = NULL;
+    char *x509_dh_file = NULL;
+    char *tls_ciphers = "DES-CBC3-SHA";    
 
-    spice_screen->command_cond = g_cond_new();
-    spice_screen->command_mutex = g_mutex_new();
+    spice_screen->width = width;
+    spice_screen->height = height;
+
+    g_cond_init(&spice_screen->command_cond);
+    g_mutex_init(&spice_screen->command_mutex);
 
     spice_screen->on_client_connected = client_connected,
     spice_screen->on_client_disconnected = client_disconnected,
@@ -712,12 +779,38 @@ spice_screen_new(SpiceCoreInterface *core, guint timeout)
     spice_screen->core = core;
     spice_screen->server = server;
 
-    spice_screen->cursor_notify = NOTIFY_CURSOR_BATCH;
+    if (opts->addr) {
+        printf("listening on '%s:%d' (TLS)\n", opts->addr, opts->port);
+        spice_server_set_addr(server, opts->addr, 0);
+    } else {
+        printf("listening on '*:%d' (TLS)\n", opts->port);
+    }
 
-    printf("listening on port %d (unsecure)\n", port);
+    // spice_server_set_port(spice_server, port);
+    //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF);
 
-    spice_server_set_port(server, port);
-    spice_server_set_noauth(server);
+    spice_server_set_tls(server, opts->port,
+                         x509_cacert_file,
+                         x509_cert_file,
+                         x509_key_file,
+                         x509_key_password,
+                         x509_dh_file,
+                         tls_ciphers);
+
+    if (opts->noauth) {
+        spice_server_set_noauth(server);
+    } else {
+        if (opts->sasl) {
+            spice_server_set_sasl(server, 1);
+            spice_server_set_sasl_appname(server, NULL); // enforce pve auth
+            spice_server_set_sasl_callbacks(server, sasl_callbacks);
+        } else {
+            char *ticket = getenv("SPICE_TICKET");
+            if (ticket) {
+                spice_server_set_ticket(server, ticket, 300, 0, 0);
+            }
+        }
+    }
 
     int res = spice_server_init(server, core);
     if (res != 0) {
@@ -726,12 +819,29 @@ spice_screen_new(SpiceCoreInterface *core, guint timeout)
 
     cursor_init();
 
-    spice_screen->conn_timeout_timer = core->timer_add(do_conn_timeout, spice_screen);
-    spice_screen->core->timer_start(spice_screen->conn_timeout_timer, timeout*1000);
+    if (opts->timeout > 0) {
+        spice_screen->conn_timeout_timer = core->timer_add(do_conn_timeout, spice_screen);
+        spice_screen->core->timer_start(spice_screen->conn_timeout_timer, opts->timeout*1000);
+    }
 
     spice_server_add_interface(spice_screen->server, &spice_screen->qxl_instance.base);
 
-    spice_server_add_interface(server, &vdagent_sin.base);
-
     return spice_screen;
 }
+
+void 
+spice_screen_resize(SpiceScreen *spice_screen, uint32_t width,
+                    uint32_t height)
+{
+    if (spice_screen->width == width && spice_screen->height == height) {
+        return;
+    }
+
+    discard_pending_commands(spice_screen);
+
+    spice_qxl_destroy_primary_surface(&spice_screen->qxl_instance, 0);
+    create_primary_surface(spice_screen, width, height);
+
+    spice_screen_clear(spice_screen, 0, 0, width, height);
+}