#include <sys/types.h>
#include <getopt.h>
+#include "glyphs.h"
#include <spice.h>
+#include <spice/enums.h>
#include <spice/macros.h>
#include <spice/qxl_dev.h>
#include "test_display_base.h"
-//#include "red_channel.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,
+ 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff};
+int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa,
+ 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff};
+int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa,
+ 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff};
+
/* Parts cribbed from spice-display.h/.c/qxl.c */
typedef struct SimpleSpiceUpdate {
uint8_t *bitmap;
} SimpleSpiceUpdate;
-typedef struct SimpleSurfaceCmd {
- QXLCommandExt ext; // first
- QXLSurfaceCmd surface_cmd;
-} SimpleSurfaceCmd;
-
static void test_spice_destroy_update(SimpleSpiceUpdate *update)
{
if (!update) {
uint8_t *ptr = (uint8_t*)update->drawable.clip.data;
free(ptr);
}
- free(update->bitmap);
- free(update);
+ g_free(update->bitmap);
+ g_free(update);
}
#define DEFAULT_WIDTH 640
//info->group_id = MEM_SLOT_GROUP_ID;
}
-typedef struct Path {
- int t;
- int min_t;
- int max_t;
-} Path;
+// We shall now have a ring of commands, so that we can update
+// it from a separate thread - since get_command is called from
+// the worker thread, and we need to sometimes do an update_area,
+// which cannot be done from red_worker context (not via dispatcher,
+// since you get a deadlock, and it isn't designed to be done
+// any other way, so no point testing that).
-static void path_init(Path *path, int min, int max)
-{
- path->t = min;
- path->min_t = min;
- path->max_t = max;
-}
-static void path_progress(Path *path)
+static void push_command(Test *test, QXLCommandExt *ext)
{
- path->t = (path->t+1)% (path->max_t - path->min_t) + path->min_t;
-}
+ g_mutex_lock(test->command_mutex);
-Path path;
+ while (test->commands_end - test->commands_start >= COMMANDS_SIZE) {
+ g_cond_wait(test->command_cond, test->command_mutex);
+ }
+ g_assert(test->commands_end - test->commands_start < COMMANDS_SIZE);
+ test->commands[test->commands_end % COMMANDS_SIZE] = ext;
+ test->commands_end++;
+ g_mutex_unlock(test->command_mutex);
-static void draw_pos(Test *test, int t, int *x, int *y)
-{
- *y = test->primary_height*(t % SINGLE_PART)/SINGLE_PART;
- *x = ((test->primary_width/SINGLE_PART)*(t / SINGLE_PART)) % test->primary_width;
+ test->qxl_worker->wakeup(test->qxl_worker);
}
-/* bitmap and rects are freed, so they must be allocated with malloc */
+/* bitmap are freed, so they must be allocated with g_malloc */
SimpleSpiceUpdate *test_spice_create_update_from_bitmap(uint32_t surface_id,
QXLRect bbox,
- uint8_t *bitmap,
- uint32_t num_clip_rects,
- QXLRect *clip_rects)
+ uint8_t *bitmap)
{
SimpleSpiceUpdate *update;
QXLDrawable *drawable;
bh = bbox.bottom - bbox.top;
bw = bbox.right - bbox.left;
- update = calloc(sizeof(*update), 1);
+ update = g_new0(SimpleSpiceUpdate, 1);
update->bitmap = bitmap;
drawable = &update->drawable;
image = &update->image;
drawable->surface_id = surface_id;
drawable->bbox = bbox;
- if (num_clip_rects == 0) {
- drawable->clip.type = SPICE_CLIP_TYPE_NONE;
- } else {
- QXLClipRects *cmd_clip;
-
- cmd_clip = calloc(sizeof(QXLClipRects) + num_clip_rects*sizeof(QXLRect), 1);
- cmd_clip->num_rects = num_clip_rects;
- cmd_clip->chunk.data_size = num_clip_rects*sizeof(QXLRect);
- cmd_clip->chunk.prev_chunk = cmd_clip->chunk.next_chunk = 0;
- memcpy(cmd_clip + 1, clip_rects, cmd_clip->chunk.data_size);
-
- drawable->clip.type = SPICE_CLIP_TYPE_RECTS;
- drawable->clip.data = (intptr_t)cmd_clip;
-
- free(clip_rects);
- }
+ drawable->clip.type = SPICE_CLIP_TYPE_NONE;
drawable->effect = QXL_EFFECT_OPAQUE;
simple_set_release_info(&drawable->release_info, (intptr_t)update);
drawable->type = QXL_DRAW_COPY;
return update;
}
-static SimpleSpiceUpdate *test_spice_create_update_solid(uint32_t surface_id, QXLRect bbox, uint32_t color)
-{
- uint8_t *bitmap;
- uint32_t *dst;
- uint32_t bw;
- uint32_t bh;
- int i;
-
- bw = bbox.right - bbox.left;
- bh = bbox.bottom - bbox.top;
-
- bitmap = malloc(bw * bh * 4);
- dst = (uint32_t *)bitmap;
-
- for (i = 0 ; i < bh * bw ; ++i, ++dst) {
- *dst = color;
- }
-
- return test_spice_create_update_from_bitmap(surface_id, bbox, bitmap, 0, NULL);
-}
-
-static SimpleSpiceUpdate *test_spice_create_update_draw(Test *test, uint32_t surface_id, int t)
+static SimpleSpiceUpdate *test_draw_char(Test *test, int x, int y, int c, int fg, int bg)
{
int top, left;
uint8_t *dst;
uint8_t *bitmap;
int bw, bh;
- int i;
+ int i, j;
QXLRect bbox;
- draw_pos(test, t, &left, &top);
- if ((t % angle_parts) == 0) {
- c_i++;
- }
-
- if (surface_id != 0) {
- color = (color + 1) % 2;
- } else {
- color = surface_id;
- }
+ left = x*8;
+ top = y*16;
+ // printf("DRAWCHAR %d %d %d\n", left, top, c);
+
unique++;
- bw = test->primary_width/SINGLE_PART;
- bh = 48;
+ bw = 8;
+ bh = 16;
+
+ bitmap = dst = g_malloc(bw * bh * 4);
+
+ unsigned char *data = vt_font_data + c*16;
+ unsigned char d = *data;
- bitmap = dst = malloc(bw * bh * 4);
- //printf("allocated %p\n", dst);
+ g_assert(fg >= 0 && fg < 16);
+ g_assert(bg >= 0 && bg < 16);
- for (i = 0 ; i < bh * bw ; ++i, dst+=4) {
- *dst = (color+i % 255);
- *(dst+((1+c_i)%3)) = 255 - color;
- *(dst+((2+c_i)%3)) = (color * (color + i)) & 0xff;
- *(dst+((3+c_i)%3)) = 0;
+ 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;
+ }
+ d<<=1;
+ dst += 4;
+ }
}
bbox.left = left; bbox.top = top;
bbox.right = left + bw; bbox.bottom = top + bh;
- return test_spice_create_update_from_bitmap(surface_id, bbox, bitmap, 0, NULL);
+
+ return test_spice_create_update_from_bitmap(0, bbox, bitmap);
}
-static SimpleSpiceUpdate *test_spice_create_update_copy_bits(Test *test, uint32_t surface_id)
+void test_spice_scroll(Test *test, int x1, int y1, int x2, int y2, int src_x, int src_y)
{
SimpleSpiceUpdate *update;
QXLDrawable *drawable;
- int bw, bh;
- QXLRect bbox = {
- .left = 10,
- .top = 0,
- };
+ QXLRect bbox;
+
+ int surface_id = 0; // fixme
- update = calloc(sizeof(*update), 1);
+ update = g_new0(SimpleSpiceUpdate, 1);
drawable = &update->drawable;
- bw = test->primary_width/SINGLE_PART;
- bh = 48;
- bbox.right = bbox.left + bw;
- bbox.bottom = bbox.top + bh;
- //printf("allocated %p, %p\n", update, update->bitmap);
+ bbox.left = x1;
+ bbox.top = y1;
+ bbox.right = x2;
+ bbox.bottom = y2;
- drawable->surface_id = surface_id;
+ drawable->surface_id = surface_id;
drawable->bbox = bbox;
drawable->clip.type = SPICE_CLIP_TYPE_NONE;
drawable->surfaces_dest[1] = -1;
drawable->surfaces_dest[2] = -1;
- drawable->u.copy_bits.src_pos.x = 0;
- drawable->u.copy_bits.src_pos.y = 0;
+ drawable->u.copy_bits.src_pos.x = src_x;
+ drawable->u.copy_bits.src_pos.y = src_y;
set_cmd(&update->ext, QXL_CMD_DRAW, (intptr_t)drawable);
- return update;
+ push_command(test, &update->ext);
+}
+
+void test_spice_clear(Test *test, int x1, int y1, int x2, int y2)
+{
+ SimpleSpiceUpdate *update;
+ QXLDrawable *drawable;
+ QXLRect bbox;
+
+ int surface_id = 0; // fixme
+
+ update = g_new0(SimpleSpiceUpdate, 1);
+ drawable = &update->drawable;
+
+ bbox.left = x1;
+ bbox.top = y1;
+ bbox.right = x2;
+ bbox.bottom = y2;
+
+ drawable->surface_id = surface_id;
+
+ drawable->bbox = bbox;
+ drawable->clip.type = SPICE_CLIP_TYPE_NONE;
+ drawable->effect = QXL_EFFECT_OPAQUE;
+ simple_set_release_info(&drawable->release_info, (intptr_t)update);
+ drawable->type = QXL_DRAW_BLACKNESS;
+ drawable->surfaces_dest[0] = -1;
+ drawable->surfaces_dest[1] = -1;
+ drawable->surfaces_dest[2] = -1;
+
+ set_cmd(&update->ext, QXL_CMD_DRAW, (intptr_t)drawable);
+
+ push_command(test, &update->ext);
}
static void create_primary_surface(Test *test, uint32_t width,
info->n_surfaces = 1;
}
-
-// We shall now have a ring of commands, so that we can update
-// it from a separate thread - since get_command is called from
-// the worker thread, and we need to sometimes do an update_area,
-// which cannot be done from red_worker context (not via dispatcher,
-// since you get a deadlock, and it isn't designed to be done
-// any other way, so no point testing that).
-int commands_end = 0;
-int commands_start = 0;
-struct QXLCommandExt* commands[1024];
-
-#define COMMANDS_SIZE COUNT(commands)
-
-static void push_command(QXLCommandExt *ext)
-{
- g_assert(commands_end - commands_start < COMMANDS_SIZE);
- commands[commands_end % COMMANDS_SIZE] = ext;
- commands_end++;
-}
-
-static struct QXLCommandExt *get_simple_command(void)
-{
- struct QXLCommandExt *ret = commands[commands_start % COMMANDS_SIZE];
- g_assert(commands_start < commands_end);
- commands_start++;
- return ret;
-}
-
-static int get_num_commands(void)
-{
- return commands_end - commands_start;
-}
-
// called from spice_server thread (i.e. red_worker thread)
static int get_command(QXLInstance *qin, struct QXLCommandExt *ext)
{
- if (get_num_commands() == 0) {
- return FALSE;
- }
- *ext = *get_simple_command();
- return TRUE;
-}
-
-static void produce_command(Test *test)
-{
- Command *command;
- QXLWorker *qxl_worker = test->qxl_worker;
-
- g_assert(qxl_worker);
-
- if (!test->num_commands) {
- usleep(1000);
- return;
- }
+ Test *test = SPICE_CONTAINEROF(qin, Test, qxl_instance);
+ int res = FALSE;
- command = &test->commands[test->cmd_index];
- if (command->cb) {
- command->cb(test, command);
+ g_mutex_lock(test->command_mutex);
+
+ if ((test->commands_end - test->commands_start) == 0) {
+ res = FALSE;
+ goto ret;
}
- switch (command->command) {
- case SLEEP:
- printf("sleep %u seconds\n", command->sleep.secs);
- sleep(command->sleep.secs);
- break;
- case PATH_PROGRESS:
- path_progress(&path);
- break;
- case SIMPLE_UPDATE: {
- QXLRect rect = {
- .left = 0,
- .right = (test->target_surface == 0 ? test->primary_width : test->width),
- .top = 0,
- .bottom = (test->target_surface == 0 ? test->primary_height : test->height)
- };
- if (rect.right > 0 && rect.bottom > 0) {
- qxl_worker->update_area(qxl_worker, test->target_surface, &rect, NULL, 0, 1);
- }
- break;
- }
- /* Drawing commands, they all push a command to the command ring */
- case SIMPLE_COPY_BITS:
- case SIMPLE_DRAW_SOLID:
- case SIMPLE_DRAW_BITMAP:
- case SIMPLE_DRAW: {
- SimpleSpiceUpdate *update;
-
- switch (command->command) {
- case SIMPLE_COPY_BITS:
- update = test_spice_create_update_copy_bits(test, 0);
- break;
- case SIMPLE_DRAW:
- update = test_spice_create_update_draw(test, 0, path.t);
- break;
- case SIMPLE_DRAW_BITMAP:
- update = test_spice_create_update_from_bitmap(command->bitmap.surface_id,
- command->bitmap.bbox, command->bitmap.bitmap,
- command->bitmap.num_clip_rects, command->bitmap.clip_rects);
- break;
- case SIMPLE_DRAW_SOLID:
- update = test_spice_create_update_solid(command->solid.surface_id,
- command->solid.bbox, command->solid.color);
- break;
- }
- push_command(&update->ext);
- break;
- }
+ *ext = *test->commands[test->commands_start % COMMANDS_SIZE];
+ g_assert(test->commands_start < test->commands_end);
+ test->commands_start++;
+ g_cond_signal(test->command_cond);
- case DESTROY_PRIMARY:
- qxl_worker->destroy_primary_surface(qxl_worker, 0);
- break;
+ res = TRUE;
- case CREATE_PRIMARY:
- create_primary_surface(test,
- command->create_primary.width, command->create_primary.height);
- break;
- }
- test->cmd_index = (test->cmd_index + 1) % test->num_commands;
+ret:
+ g_mutex_unlock(test->command_mutex);
+ return res;
}
static int req_cmd_notification(QXLInstance *qin)
{
Test *test = SPICE_CONTAINEROF(qin, Test, qxl_instance);
- test->core->timer_start(test->wakeup_timer, test->wakeup_ms);
+ //test->core->timer_start(test->wakeup_timer, test->wakeup_ms);
return TRUE;
}
-static void do_wakeup(void *opaque)
-{
- Test *test = opaque;
- int notify;
-
- test->cursor_notify = NOTIFY_CURSOR_BATCH;
- for (notify = NOTIFY_DISPLAY_BATCH; notify > 0;--notify) {
- produce_command(test);
- }
-
- test->core->timer_start(test->wakeup_timer, test->wakeup_ms);
- test->qxl_worker->wakeup(test->qxl_worker);
-}
-
static void release_resource(QXLInstance *qin, struct QXLReleaseInfoExt release_info)
{
QXLCommandExt *ext = (QXLCommandExt*)(unsigned long)release_info.info->id;
}
}
+static int client_count = 0;
+
+static void client_connected(Test *test)
+{
+ printf("Client connected\n");
+ client_count++;
+}
+
+static void client_disconnected(Test *test)
+{
+
+ if (client_count > 0) {
+ client_count--;
+ printf("Client disconnected\n");
+ exit(0); // fixme: cleanup?
+ }
+}
+
+static void do_conn_timeout(void *opaque)
+{
+ Test *test = opaque;
+
+ if (client_count <= 0) {
+ printf("do_conn_timeout\n");
+ exit (0); // fixme: cleanup?
+ }
+}
+
+
QXLInterface display_sif = {
.base = {
.type = SPICE_INTERFACE_QXL,
static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
{
- printf("%s: %d\n", __func__, len);
+// printf("%s: %d\n", __func__, len);
return len;
}
static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
{
- printf("%s: %d\n", __func__, len);
+// printf("%s: %d\n", __func__, len);
return 0;
}
static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
{
- printf("%s: %d\n", __func__, connected);
+// printf("%s: %d\n", __func__, connected);
}
static SpiceCharDeviceInterface vdagent_sif = {
spice_server_add_interface(server, &vdagent_sin.base);
}
-void test_set_simple_command_list(Test *test, int *simple_commands, int num_commands)
+static int my_charcode = 65;
+static int my_posx = 0;
+static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
{
- int i;
+ Test *test = SPICE_CONTAINEROF(sin, Test, keyboard_sin);
+
+ printf("KEYCODE %u %p\n", frag, test);
+
+}
- /* FIXME: leaks */
- test->commands = malloc(sizeof(*test->commands) * num_commands);
- memset(test->commands, 0, sizeof(*test->commands) * num_commands);
- test->num_commands = num_commands;
- for (i = 0 ; i < num_commands; ++i) {
- test->commands[i].command = simple_commands[i];
+void test_draw_update_char(Test *test, int x, int y, int c, TextAttributes attrib)
+{
+ int fg, bg;
+
+ if (attrib.invers) {
+ bg = attrib.fgcol;
+ fg = attrib.bgcol;
+ } else {
+ bg = attrib.bgcol;
+ fg = attrib.fgcol;
+ }
+
+ if (attrib.bold) {
+ fg += 8;
}
+
+ // unsuported attributes = (attrib.blink || attrib.unvisible)
+
+ //if (attrib.uline) {
+ //rfbDrawLine (vt->screen, rx, ry + 14, rxe, ry + 14, fg);
+ //}
+
+ SimpleSpiceUpdate *update;
+ update = test_draw_char(test, x, y, c, fg, bg);
+ push_command(test, &update->ext);
}
-void test_set_command_list(Test *test, Command *commands, int num_commands)
+static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
{
- test->commands = commands;
- test->num_commands = num_commands;
+ return 0;
}
+static SpiceKbdInterface 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_scan_freg = kbd_push_key,
+ .get_leds = kbd_get_leds,
+};
+
+void test_add_keyboard_interface(Test* test)
+{
+ spice_server_add_interface(test->server, &test->keyboard_sin.base);
+}
Test *test_new(SpiceCoreInterface *core)
{
Test *test = g_new0(Test, 1);
SpiceServer* server = spice_server_new();
+ test->command_cond = g_cond_new();
+ test->command_mutex = g_mutex_new();
+
+ test->on_client_connected = client_connected,
+ test->on_client_disconnected = client_disconnected,
+
test->qxl_instance.base.sif = &display_sif.base;
test->qxl_instance.id = 0;
+ test->keyboard_sin.base.sif = &keyboard_sif.base;
+
test->core = core;
test->server = server;
- test->wakeup_ms = 50;
+
test->cursor_notify = NOTIFY_CURSOR_BATCH;
// some common initialization for all display tests
printf("TESTER: listening on port %d (unsecure)\n", port);
}
cursor_init();
- path_init(&path, 0, angle_parts);
test->has_secondary = 0;
- test->wakeup_timer = core->timer_add(do_wakeup, test);
+
+ int timeout = 10; // max time to wait for client connection
+ test->conn_timeout_timer = core->timer_add(do_conn_timeout, test);
+ test->core->timer_start(test->conn_timeout_timer, timeout*1000);
+
return test;
}