#include <spice/enums.h>
#include <spice/macros.h>
#include <spice/qxl_dev.h>
+#include <spice/vd_agent.h>
#include "glyphs.h"
#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,
/* 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
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);
-static int unique = 1;
+ 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;
static void
set_cmd(QXLCommandExt *ext, uint32_t type, QXLPHYSICAL data)
static void
push_command(SpiceScreen *spice_screen, QXLCommandExt *ext)
{
+ int need_wakeup = 1;
+
g_mutex_lock(spice_screen->command_mutex);
while (spice_screen->commands_end - spice_screen->commands_start >= COMMANDS_SIZE) {
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++;
+ if (need_wakeup) {
+ 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 */
static SimpleSpiceUpdate *
-spice_screen_update_from_bitmap_cmd(uint32_t surface_id, QXLRect bbox, uint8_t *bitmap)
+spice_screen_update_from_bitmap_cmd(uint32_t surface_id, QXLRect bbox, uint8_t *bitmap, int cache_id)
{
SimpleSpiceUpdate *update;
QXLDrawable *drawable;
drawable->u.copy.src_area.right = bw;
drawable->u.copy.src_area.bottom = bh;
- QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, unique);
+ 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);
+ }
image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN;
image->bitmap.stride = bw * 4;
static SimpleSpiceUpdate *
spice_screen_draw_char_cmd(SpiceScreen *spice_screen, int x, int y, int c,
- int fg, int bg)
+ int fg, int bg, gboolean uline)
{
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;
top = y*16;
- unique++;
-
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++) {
- for (i = 0; i < 8; i++) {
- if ((i&7) == 0) {
- d=*data;
- data++;
- }
- if (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;
+ 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;
}
- 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;
bbox.right = left + bw; bbox.bottom = top + bh;
- return spice_screen_update_from_bitmap_cmd(0, bbox, bitmap);
+ return spice_screen_update_from_bitmap_cmd(0, bbox, bitmap, cache_id);
}
void
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;
spice_screen->width = width;
spice_screen->height = height;
+ spice_screen->cursor_set = 0;
+
qxl_worker->create_primary_surface(qxl_worker, 0, &surface);
}
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);
+ create_primary_surface(spice_screen, spice_screen->width, spice_screen->height);
spice_screen->qxl_worker->start(spice_screen->qxl_worker);
}
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)
{
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
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;
{
SpiceScreen *spice_screen = SPICE_CONTAINEROF(qin, SpiceScreen, qxl_instance);
- DPRINTF(1, "%s: present %d caps %d", __func__, client_present, caps[0]);
+ DPRINTF(1, "present %d caps %d", client_present, caps[0]);
if (spice_screen->on_client_connected && client_present) {
spice_screen->on_client_connected(spice_screen);
{
client_count++;
- DPRINTF(1, "%s: client_count = %d", __func__, client_count);
+ DPRINTF(1, "client_count = %d", client_count);
}
static void
{
if (client_count > 0) {
client_count--;
- DPRINTF(1, "%s: client_count = %d", __func__, client_count);
+ DPRINTF(1, "client_count = %d", client_count);
exit(0); // fixme: cleanup?
}
}
}
}
+
QXLInterface display_sif = {
.base = {
.type = SPICE_INTERFACE_QXL,
.set_client_capabilities = set_client_capabilities,
};
-void
-spice_screen_add_display_interface(SpiceScreen* spice_screen)
-{
- spice_server_add_interface(spice_screen->server, &spice_screen->qxl_instance.base);
-}
-
-/* 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_add_agent_interface(SpiceServer *server)
-{
- spice_server_add_interface(server, &vdagent_sin.base);
-}
void
-spice_screen_draw_char(SpiceScreen *spice_screen, int x, int y, gunichar 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 {
// unsuported attributes = (attrib.blink || attrib.unvisible)
- // fixme:
- //if (attrib.uline) {
- //rfbDrawLine (vt->screen, rx, ry + 14, rxe, ry + 14, fg);
- //}
-
int c = vt_fontmap[ch];
SimpleSpiceUpdate *update;
- update = spice_screen_draw_char_cmd(spice_screen, x, y, c, fg, bg);
+ update = spice_screen_draw_char_cmd(spice_screen, x, y, c, fg, bg, attrib.uline);
push_command(spice_screen, &update->ext);
}
SpiceScreen *
-spice_screen_new(SpiceCoreInterface *core)
+spice_screen_new(SpiceCoreInterface *core, uint32_t width, uint32_t height, guint timeout)
{
int port = 5912;
SpiceScreen *spice_screen = g_new0(SpiceScreen, 1);
SpiceServer* server = spice_server_new();
+ spice_screen->width = width;
+ spice_screen->height = height;
+
spice_screen->command_cond = g_cond_new();
spice_screen->command_mutex = g_mutex_new();
spice_screen->core = core;
spice_screen->server = server;
- spice_screen->cursor_notify = NOTIFY_CURSOR_BATCH;
-
printf("listening on port %d (unsecure)\n", port);
spice_server_set_port(server, port);
cursor_init();
- int timeout = 10; // max time to wait for client connection
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);
+ spice_server_add_interface(spice_screen->server, &spice_screen->qxl_instance.base);
+
return spice_screen;
}
+
+void
+spice_screen_resize(SpiceScreen *spice_screen, uint32_t width,
+ uint32_t height)
+{
+ QXLWorker *qxl_worker = spice_screen->qxl_worker;
+
+ if (spice_screen->width == width && spice_screen->height == height) {
+ return;
+ }
+
+ discard_pending_commands(spice_screen);
+
+ qxl_worker->destroy_primary_surface(qxl_worker, 0);
+
+ create_primary_surface(spice_screen, width, height);
+
+ spice_screen_clear(spice_screen, 0, 0, width, height);
+}