X-Git-Url: https://git.proxmox.com/?p=spiceterm.git;a=blobdiff_plain;f=screen.c;h=6f253d2047042832b6563bc23b9781c226380ebd;hp=231fbbe5ec9f6d2db821755d0c28b352943231ae;hb=b52b953498955f8ea4c8ec2b348683a13bd4c228;hpb=11ba14dc326bdb80175f841625e3e02a1fede2de diff --git a/screen.c b/screen.c index 231fbbe..6f253d2 100644 --- a/screen.c +++ b/screen.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #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); +}