]> git.proxmox.com Git - spiceterm.git/blame - test_display_base.c
exit if spice_server_init fails (socket already in use?)
[spiceterm.git] / test_display_base.c
CommitLineData
cc04455b
DM
1#include <stdlib.h>
2#include <math.h>
3#include <string.h>
4#include <stdio.h>
5#include <unistd.h>
6#include <signal.h>
7#include <wait.h>
8#include <sys/select.h>
9#include <sys/types.h>
10#include <getopt.h>
11
12#include <spice.h>
13#include <spice/macros.h>
14#include <spice/qxl_dev.h>
15
16#include "test_display_base.h"
17//#include "red_channel.h"
18
19#define MEM_SLOT_GROUP_ID 0
20
21#define NOTIFY_DISPLAY_BATCH (SINGLE_PART/2)
22#define NOTIFY_CURSOR_BATCH 10
23
24/* Parts cribbed from spice-display.h/.c/qxl.c */
25
26typedef struct SimpleSpiceUpdate {
27 QXLCommandExt ext; // first
28 QXLDrawable drawable;
29 QXLImage image;
30 uint8_t *bitmap;
31} SimpleSpiceUpdate;
32
33typedef struct SimpleSurfaceCmd {
34 QXLCommandExt ext; // first
35 QXLSurfaceCmd surface_cmd;
36} SimpleSurfaceCmd;
37
38static void test_spice_destroy_update(SimpleSpiceUpdate *update)
39{
40 if (!update) {
41 return;
42 }
43 if (update->drawable.clip.type != SPICE_CLIP_TYPE_NONE) {
44 uint8_t *ptr = (uint8_t*)update->drawable.clip.data;
45 free(ptr);
46 }
47 free(update->bitmap);
48 free(update);
49}
50
51#define DEFAULT_WIDTH 640
52#define DEFAULT_HEIGHT 320
53
54#define SINGLE_PART 4
55static const int angle_parts = 64 / SINGLE_PART;
56static int unique = 1;
57static int color = -1;
58static int c_i = 0;
59
60/* Used for automated tests */
61static int control = 3; //used to know when we can take a screenshot
62static int rects = 16; //number of rects that will be draw
63static int has_automated_tests = 0; //automated test flag
64
65__attribute__((noreturn))
66static void sigchld_handler(int signal_num) // wait for the child process and exit
67{
68 int status;
69 wait(&status);
70 exit(0);
71}
72
73static void regression_test(void)
74{
75 pid_t pid;
76
77 if (--rects != 0) {
78 return;
79 }
80
81 rects = 16;
82
83 if (--control != 0) {
84 return;
85 }
86
87 pid = fork();
88 if (pid == 0) {
89 char buf[PATH_MAX];
90 char *envp[] = {buf, NULL};
91
92 snprintf(buf, sizeof(buf), "PATH=%s", getenv("PATH"));
93 execve("regression_test.py", NULL, envp);
94 } else if (pid > 0) {
95 return;
96 }
97}
98
99static void set_cmd(QXLCommandExt *ext, uint32_t type, QXLPHYSICAL data)
100{
101 ext->cmd.type = type;
102 ext->cmd.data = data;
103 ext->cmd.padding = 0;
104 ext->group_id = MEM_SLOT_GROUP_ID;
105 ext->flags = 0;
106}
107
108static void simple_set_release_info(QXLReleaseInfo *info, intptr_t ptr)
109{
110 info->id = ptr;
111 //info->group_id = MEM_SLOT_GROUP_ID;
112}
113
114typedef struct Path {
115 int t;
116 int min_t;
117 int max_t;
118} Path;
119
120static void path_init(Path *path, int min, int max)
121{
122 path->t = min;
123 path->min_t = min;
124 path->max_t = max;
125}
126
127static void path_progress(Path *path)
128{
129 path->t = (path->t+1)% (path->max_t - path->min_t) + path->min_t;
130}
131
132Path path;
133
134static void draw_pos(Test *test, int t, int *x, int *y)
135{
136#ifdef CIRCLE
137 *y = test->primary_height/2 + (test->primary_height/3)*cos(t*2*M_PI/angle_parts);
138 *x = test->primary_width/2 + (test->primary_width/3)*sin(t*2*M_PI/angle_parts);
139#else
140 *y = test->primary_height*(t % SINGLE_PART)/SINGLE_PART;
141 *x = ((test->primary_width/SINGLE_PART)*(t / SINGLE_PART)) % test->primary_width;
142#endif
143}
144
145/* bitmap and rects are freed, so they must be allocated with malloc */
146SimpleSpiceUpdate *test_spice_create_update_from_bitmap(uint32_t surface_id,
147 QXLRect bbox,
148 uint8_t *bitmap,
149 uint32_t num_clip_rects,
150 QXLRect *clip_rects)
151{
152 SimpleSpiceUpdate *update;
153 QXLDrawable *drawable;
154 QXLImage *image;
155 uint32_t bw, bh;
156
157 bh = bbox.bottom - bbox.top;
158 bw = bbox.right - bbox.left;
159
160 update = calloc(sizeof(*update), 1);
161 update->bitmap = bitmap;
162 drawable = &update->drawable;
163 image = &update->image;
164
165 drawable->surface_id = surface_id;
166
167 drawable->bbox = bbox;
168 if (num_clip_rects == 0) {
169 drawable->clip.type = SPICE_CLIP_TYPE_NONE;
170 } else {
171 QXLClipRects *cmd_clip;
172
173 cmd_clip = calloc(sizeof(QXLClipRects) + num_clip_rects*sizeof(QXLRect), 1);
174 cmd_clip->num_rects = num_clip_rects;
175 cmd_clip->chunk.data_size = num_clip_rects*sizeof(QXLRect);
176 cmd_clip->chunk.prev_chunk = cmd_clip->chunk.next_chunk = 0;
177 memcpy(cmd_clip + 1, clip_rects, cmd_clip->chunk.data_size);
178
179 drawable->clip.type = SPICE_CLIP_TYPE_RECTS;
180 drawable->clip.data = (intptr_t)cmd_clip;
181
182 free(clip_rects);
183 }
184 drawable->effect = QXL_EFFECT_OPAQUE;
185 simple_set_release_info(&drawable->release_info, (intptr_t)update);
186 drawable->type = QXL_DRAW_COPY;
187 drawable->surfaces_dest[0] = -1;
188 drawable->surfaces_dest[1] = -1;
189 drawable->surfaces_dest[2] = -1;
190
191 drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT;
192 drawable->u.copy.src_bitmap = (intptr_t)image;
193 drawable->u.copy.src_area.right = bw;
194 drawable->u.copy.src_area.bottom = bh;
195
196 QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, unique);
197 image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
198 image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN;
199 image->bitmap.stride = bw * 4;
200 image->descriptor.width = image->bitmap.x = bw;
201 image->descriptor.height = image->bitmap.y = bh;
202 image->bitmap.data = (intptr_t)bitmap;
203 image->bitmap.palette = 0;
204 image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
205
206 set_cmd(&update->ext, QXL_CMD_DRAW, (intptr_t)drawable);
207
208 return update;
209}
210
211static SimpleSpiceUpdate *test_spice_create_update_solid(uint32_t surface_id, QXLRect bbox, uint32_t color)
212{
213 uint8_t *bitmap;
214 uint32_t *dst;
215 uint32_t bw;
216 uint32_t bh;
217 int i;
218
219 bw = bbox.right - bbox.left;
220 bh = bbox.bottom - bbox.top;
221
222 bitmap = malloc(bw * bh * 4);
223 dst = (uint32_t *)bitmap;
224
225 for (i = 0 ; i < bh * bw ; ++i, ++dst) {
226 *dst = color;
227 }
228
229 return test_spice_create_update_from_bitmap(surface_id, bbox, bitmap, 0, NULL);
230}
231
232static SimpleSpiceUpdate *test_spice_create_update_draw(Test *test, uint32_t surface_id, int t)
233{
234 int top, left;
235 uint8_t *dst;
236 uint8_t *bitmap;
237 int bw, bh;
238 int i;
239 QXLRect bbox;
240
241 draw_pos(test, t, &left, &top);
242 if ((t % angle_parts) == 0) {
243 c_i++;
244 }
245
246 if (surface_id != 0) {
247 color = (color + 1) % 2;
248 } else {
249 color = surface_id;
250 }
251
252 unique++;
253
254 bw = test->primary_width/SINGLE_PART;
255 bh = 48;
256
257 bitmap = dst = malloc(bw * bh * 4);
258 //printf("allocated %p\n", dst);
259
260 for (i = 0 ; i < bh * bw ; ++i, dst+=4) {
261 *dst = (color+i % 255);
262 *(dst+((1+c_i)%3)) = 255 - color;
263 *(dst+((2+c_i)%3)) = (color * (color + i)) & 0xff;
264 *(dst+((3+c_i)%3)) = 0;
265 }
266
267 bbox.left = left; bbox.top = top;
268 bbox.right = left + bw; bbox.bottom = top + bh;
269 return test_spice_create_update_from_bitmap(surface_id, bbox, bitmap, 0, NULL);
270}
271
272static SimpleSpiceUpdate *test_spice_create_update_copy_bits(Test *test, uint32_t surface_id)
273{
274 SimpleSpiceUpdate *update;
275 QXLDrawable *drawable;
276 int bw, bh;
277 QXLRect bbox = {
278 .left = 10,
279 .top = 0,
280 };
281
282 update = calloc(sizeof(*update), 1);
283 drawable = &update->drawable;
284
285 bw = test->primary_width/SINGLE_PART;
286 bh = 48;
287 bbox.right = bbox.left + bw;
288 bbox.bottom = bbox.top + bh;
289 //printf("allocated %p, %p\n", update, update->bitmap);
290
291 drawable->surface_id = surface_id;
292
293 drawable->bbox = bbox;
294 drawable->clip.type = SPICE_CLIP_TYPE_NONE;
295 drawable->effect = QXL_EFFECT_OPAQUE;
296 simple_set_release_info(&drawable->release_info, (intptr_t)update);
297 drawable->type = QXL_COPY_BITS;
298 drawable->surfaces_dest[0] = -1;
299 drawable->surfaces_dest[1] = -1;
300 drawable->surfaces_dest[2] = -1;
301
302 drawable->u.copy_bits.src_pos.x = 0;
303 drawable->u.copy_bits.src_pos.y = 0;
304
305 set_cmd(&update->ext, QXL_CMD_DRAW, (intptr_t)drawable);
306
307 return update;
308}
309
310static int format_to_bpp(int format)
311{
312 switch (format) {
313 case SPICE_SURFACE_FMT_8_A:
314 return 1;
315 case SPICE_SURFACE_FMT_16_555:
316 case SPICE_SURFACE_FMT_16_565:
317 return 2;
318 case SPICE_SURFACE_FMT_32_xRGB:
319 case SPICE_SURFACE_FMT_32_ARGB:
320 return 4;
321 }
322 abort();
323}
324
325static SimpleSurfaceCmd *create_surface(int surface_id, int format, int width, int height, uint8_t *data)
326{
327 SimpleSurfaceCmd *simple_cmd = calloc(sizeof(SimpleSurfaceCmd), 1);
328 QXLSurfaceCmd *surface_cmd = &simple_cmd->surface_cmd;
329 int bpp = format_to_bpp(format);
330
331 set_cmd(&simple_cmd->ext, QXL_CMD_SURFACE, (intptr_t)surface_cmd);
332 simple_set_release_info(&surface_cmd->release_info, (intptr_t)simple_cmd);
333 surface_cmd->type = QXL_SURFACE_CMD_CREATE;
334 surface_cmd->flags = 0; // ?
335 surface_cmd->surface_id = surface_id;
336 surface_cmd->u.surface_create.format = format;
337 surface_cmd->u.surface_create.width = width;
338 surface_cmd->u.surface_create.height = height;
339 surface_cmd->u.surface_create.stride = -width * bpp;
340 surface_cmd->u.surface_create.data = (intptr_t)data;
341 return simple_cmd;
342}
343
344static SimpleSurfaceCmd *destroy_surface(int surface_id)
345{
346 SimpleSurfaceCmd *simple_cmd = calloc(sizeof(SimpleSurfaceCmd), 1);
347 QXLSurfaceCmd *surface_cmd = &simple_cmd->surface_cmd;
348
349 set_cmd(&simple_cmd->ext, QXL_CMD_SURFACE, (intptr_t)surface_cmd);
350 simple_set_release_info(&surface_cmd->release_info, (intptr_t)simple_cmd);
351 surface_cmd->type = QXL_SURFACE_CMD_DESTROY;
352 surface_cmd->flags = 0; // ?
353 surface_cmd->surface_id = surface_id;
354 return simple_cmd;
355}
356
357static void create_primary_surface(Test *test, uint32_t width,
358 uint32_t height)
359{
360 QXLWorker *qxl_worker = test->qxl_worker;
361 QXLDevSurfaceCreate surface = { 0, };
362
363 g_assert(height <= MAX_HEIGHT);
364 g_assert(width <= MAX_WIDTH);
365 g_assert(height > 0);
366 g_assert(width > 0);
367
368 surface.format = SPICE_SURFACE_FMT_32_xRGB;
369 surface.width = test->primary_width = width;
370 surface.height = test->primary_height = height;
371 surface.stride = -width * 4; /* negative? */
372 surface.mouse_mode = TRUE; /* unused by red_worker */
373 surface.flags = 0;
374 surface.type = 0; /* unused by red_worker */
375 surface.position = 0; /* unused by red_worker */
376 surface.mem = (uint64_t)&test->primary_surface;
377 surface.group_id = MEM_SLOT_GROUP_ID;
378
379 test->width = width;
380 test->height = height;
381
382 qxl_worker->create_primary_surface(qxl_worker, 0, &surface);
383}
384
385QXLDevMemSlot slot = {
386.slot_group_id = MEM_SLOT_GROUP_ID,
387.slot_id = 0,
388.generation = 0,
389.virt_start = 0,
390.virt_end = ~0,
391.addr_delta = 0,
392.qxl_ram_size = ~0,
393};
394
395static void attache_worker(QXLInstance *qin, QXLWorker *_qxl_worker)
396{
397 Test *test = SPICE_CONTAINEROF(qin, Test, qxl_instance);
398
399 if (test->qxl_worker) {
400 if (test->qxl_worker != _qxl_worker)
401 printf("%s ignored, %p is set, ignoring new %p\n", __func__,
402 test->qxl_worker, _qxl_worker);
403 else
404 printf("%s ignored, redundant\n", __func__);
405 return;
406 }
407 printf("%s\n", __func__);
408 test->qxl_worker = _qxl_worker;
409 test->qxl_worker->add_memslot(test->qxl_worker, &slot);
410 create_primary_surface(test, DEFAULT_WIDTH, DEFAULT_HEIGHT);
411 test->qxl_worker->start(test->qxl_worker);
412}
413
414static void set_compression_level(QXLInstance *qin, int level)
415{
416 printf("%s\n", __func__);
417}
418
419static void set_mm_time(QXLInstance *qin, uint32_t mm_time)
420{
421}
422
423// we now have a secondary surface
424#define MAX_SURFACE_NUM 2
425
426static void get_init_info(QXLInstance *qin, QXLDevInitInfo *info)
427{
428 memset(info, 0, sizeof(*info));
429 info->num_memslots = 1;
430 info->num_memslots_groups = 1;
431 info->memslot_id_bits = 1;
432 info->memslot_gen_bits = 1;
433 info->n_surfaces = MAX_SURFACE_NUM;
434}
435
436// We shall now have a ring of commands, so that we can update
437// it from a separate thread - since get_command is called from
438// the worker thread, and we need to sometimes do an update_area,
439// which cannot be done from red_worker context (not via dispatcher,
440// since you get a deadlock, and it isn't designed to be done
441// any other way, so no point testing that).
442int commands_end = 0;
443int commands_start = 0;
444struct QXLCommandExt* commands[1024];
445
446#define COMMANDS_SIZE COUNT(commands)
447
448static void push_command(QXLCommandExt *ext)
449{
450 g_assert(commands_end - commands_start < COMMANDS_SIZE);
451 commands[commands_end % COMMANDS_SIZE] = ext;
452 commands_end++;
453}
454
455static struct QXLCommandExt *get_simple_command(void)
456{
457 struct QXLCommandExt *ret = commands[commands_start % COMMANDS_SIZE];
458 g_assert(commands_start < commands_end);
459 commands_start++;
460 return ret;
461}
462
463static int get_num_commands(void)
464{
465 return commands_end - commands_start;
466}
467
468// called from spice_server thread (i.e. red_worker thread)
469static int get_command(QXLInstance *qin, struct QXLCommandExt *ext)
470{
471 if (get_num_commands() == 0) {
472 return FALSE;
473 }
474 *ext = *get_simple_command();
475 return TRUE;
476}
477
478static void produce_command(Test *test)
479{
480 Command *command;
481 QXLWorker *qxl_worker = test->qxl_worker;
482
483 g_assert(qxl_worker);
484
485 if (test->has_secondary)
486 test->target_surface = 1;
487
488 if (!test->num_commands) {
489 usleep(1000);
490 return;
491 }
492
493 command = &test->commands[test->cmd_index];
494 if (command->cb) {
495 command->cb(test, command);
496 }
497 switch (command->command) {
498 case SLEEP:
499 printf("sleep %u seconds\n", command->sleep.secs);
500 sleep(command->sleep.secs);
501 break;
502 case PATH_PROGRESS:
503 path_progress(&path);
504 break;
505 case SIMPLE_UPDATE: {
506 QXLRect rect = {
507 .left = 0,
508 .right = (test->target_surface == 0 ? test->primary_width : test->width),
509 .top = 0,
510 .bottom = (test->target_surface == 0 ? test->primary_height : test->height)
511 };
512 if (rect.right > 0 && rect.bottom > 0) {
513 qxl_worker->update_area(qxl_worker, test->target_surface, &rect, NULL, 0, 1);
514 }
515 break;
516 }
517
518 /* Drawing commands, they all push a command to the command ring */
519 case SIMPLE_COPY_BITS:
520 case SIMPLE_DRAW_SOLID:
521 case SIMPLE_DRAW_BITMAP:
522 case SIMPLE_DRAW: {
523 SimpleSpiceUpdate *update;
524
525 if (has_automated_tests)
526 {
527 if (control == 0) {
528 return;
529 }
530
531 regression_test();
532 }
533
534 switch (command->command) {
535 case SIMPLE_COPY_BITS:
536 update = test_spice_create_update_copy_bits(test, 0);
537 break;
538 case SIMPLE_DRAW:
539 update = test_spice_create_update_draw(test, 0, path.t);
540 break;
541 case SIMPLE_DRAW_BITMAP:
542 update = test_spice_create_update_from_bitmap(command->bitmap.surface_id,
543 command->bitmap.bbox, command->bitmap.bitmap,
544 command->bitmap.num_clip_rects, command->bitmap.clip_rects);
545 break;
546 case SIMPLE_DRAW_SOLID:
547 update = test_spice_create_update_solid(command->solid.surface_id,
548 command->solid.bbox, command->solid.color);
549 break;
550 }
551 push_command(&update->ext);
552 break;
553 }
554
555 case SIMPLE_CREATE_SURFACE: {
556 SimpleSurfaceCmd *update;
557 if (command->create_surface.data) {
558 g_assert(command->create_surface.surface_id > 0);
559 g_assert(command->create_surface.surface_id < MAX_SURFACE_NUM);
560 g_assert(command->create_surface.surface_id == 1);
561 update = create_surface(command->create_surface.surface_id,
562 command->create_surface.format,
563 command->create_surface.width,
564 command->create_surface.height,
565 command->create_surface.data);
566 } else {
567 update = create_surface(test->target_surface, SPICE_SURFACE_FMT_32_xRGB,
568 SURF_WIDTH, SURF_HEIGHT,
569 test->secondary_surface);
570 }
571 push_command(&update->ext);
572 test->has_secondary = 1;
573 break;
574 }
575
576 case SIMPLE_DESTROY_SURFACE: {
577 SimpleSurfaceCmd *update;
578 test->has_secondary = 0;
579 update = destroy_surface(test->target_surface);
580 test->target_surface = 0;
581 push_command(&update->ext);
582 break;
583 }
584
585 case DESTROY_PRIMARY:
586 qxl_worker->destroy_primary_surface(qxl_worker, 0);
587 break;
588
589 case CREATE_PRIMARY:
590 create_primary_surface(test,
591 command->create_primary.width, command->create_primary.height);
592 break;
593 }
594 test->cmd_index = (test->cmd_index + 1) % test->num_commands;
595}
596
597static int req_cmd_notification(QXLInstance *qin)
598{
599 Test *test = SPICE_CONTAINEROF(qin, Test, qxl_instance);
600
601 test->core->timer_start(test->wakeup_timer, test->wakeup_ms);
602 return TRUE;
603}
604
605static void do_wakeup(void *opaque)
606{
607 Test *test = opaque;
608 int notify;
609
610 test->cursor_notify = NOTIFY_CURSOR_BATCH;
611 for (notify = NOTIFY_DISPLAY_BATCH; notify > 0;--notify) {
612 produce_command(test);
613 }
614
615 test->core->timer_start(test->wakeup_timer, test->wakeup_ms);
616 test->qxl_worker->wakeup(test->qxl_worker);
617}
618
619static void release_resource(QXLInstance *qin, struct QXLReleaseInfoExt release_info)
620{
621 QXLCommandExt *ext = (QXLCommandExt*)(unsigned long)release_info.info->id;
622 //printf("%s\n", __func__);
623 g_assert(release_info.group_id == MEM_SLOT_GROUP_ID);
624 switch (ext->cmd.type) {
625 case QXL_CMD_DRAW:
626 test_spice_destroy_update((void*)ext);
627 break;
628 case QXL_CMD_SURFACE:
629 free(ext);
630 break;
631 case QXL_CMD_CURSOR: {
632 QXLCursorCmd *cmd = (QXLCursorCmd *)(unsigned long)ext->cmd.data;
633 if (cmd->type == QXL_CURSOR_SET) {
634 free(cmd);
635 }
636 free(ext);
637 break;
638 }
639 default:
640 abort();
641 }
642}
643
644#define CURSOR_WIDTH 32
645#define CURSOR_HEIGHT 32
646
647static struct {
648 QXLCursor cursor;
649 uint8_t data[CURSOR_WIDTH * CURSOR_HEIGHT * 4]; // 32bit per pixel
650} cursor;
651
652static void cursor_init()
653{
654 cursor.cursor.header.unique = 0;
655 cursor.cursor.header.type = SPICE_CURSOR_TYPE_COLOR32;
656 cursor.cursor.header.width = CURSOR_WIDTH;
657 cursor.cursor.header.height = CURSOR_HEIGHT;
658 cursor.cursor.header.hot_spot_x = 0;
659 cursor.cursor.header.hot_spot_y = 0;
660 cursor.cursor.data_size = CURSOR_WIDTH * CURSOR_HEIGHT * 4;
661
662 // X drivers addes it to the cursor size because it could be
663 // cursor data information or another cursor related stuffs.
664 // Otherwise, the code will break in client/cursor.cpp side,
665 // that expect the data_size plus cursor information.
666 // Blame cursor protocol for this. :-)
667 cursor.cursor.data_size += 128;
668 cursor.cursor.chunk.data_size = cursor.cursor.data_size;
669 cursor.cursor.chunk.prev_chunk = cursor.cursor.chunk.next_chunk = 0;
670}
671
672static int get_cursor_command(QXLInstance *qin, struct QXLCommandExt *ext)
673{
674 Test *test = SPICE_CONTAINEROF(qin, Test, qxl_instance);
675 static int color = 0;
676 static int set = 1;
677 static int x = 0, y = 0;
678 QXLCursorCmd *cursor_cmd;
679 QXLCommandExt *cmd;
680
681 if (!test->cursor_notify) {
682 return FALSE;
683 }
684
685 test->cursor_notify--;
686 cmd = calloc(sizeof(QXLCommandExt), 1);
687 cursor_cmd = calloc(sizeof(QXLCursorCmd), 1);
688
689 cursor_cmd->release_info.id = (unsigned long)cmd;
690
691 if (set) {
692 cursor_cmd->type = QXL_CURSOR_SET;
693 cursor_cmd->u.set.position.x = 0;
694 cursor_cmd->u.set.position.y = 0;
695 cursor_cmd->u.set.visible = TRUE;
696 cursor_cmd->u.set.shape = (unsigned long)&cursor;
697 // Only a white rect (32x32) as cursor
698 memset(cursor.data, 255, sizeof(cursor.data));
699 set = 0;
700 } else {
701 cursor_cmd->type = QXL_CURSOR_MOVE;
702 cursor_cmd->u.position.x = x++ % test->primary_width;
703 cursor_cmd->u.position.y = y++ % test->primary_height;
704 }
705
706 cmd->cmd.data = (unsigned long)cursor_cmd;
707 cmd->cmd.type = QXL_CMD_CURSOR;
708 cmd->group_id = MEM_SLOT_GROUP_ID;
709 cmd->flags = 0;
710 *ext = *cmd;
711 //printf("%s\n", __func__);
712 return TRUE;
713}
714
715static int req_cursor_notification(QXLInstance *qin)
716{
717 printf("%s\n", __func__);
718 return TRUE;
719}
720
721static void notify_update(QXLInstance *qin, uint32_t update_id)
722{
723 printf("%s\n", __func__);
724}
725
726static int flush_resources(QXLInstance *qin)
727{
728 printf("%s\n", __func__);
729 return TRUE;
730}
731
732static int client_monitors_config(QXLInstance *qin,
733 VDAgentMonitorsConfig *monitors_config)
734{
735 if (!monitors_config) {
736 printf("%s: NULL monitors_config\n", __func__);
737 } else {
738 printf("%s: %d\n", __func__, monitors_config->num_of_monitors);
739 }
740 return 0;
741}
742
743static void set_client_capabilities(QXLInstance *qin,
744 uint8_t client_present,
745 uint8_t caps[58])
746{
747 Test *test = SPICE_CONTAINEROF(qin, Test, qxl_instance);
748
749 printf("%s: present %d caps %d\n", __func__, client_present, caps[0]);
750 if (test->on_client_connected && client_present) {
751 test->on_client_connected(test);
752 }
753 if (test->on_client_disconnected && !client_present) {
754 test->on_client_disconnected(test);
755 }
756}
757
758QXLInterface display_sif = {
759 .base = {
760 .type = SPICE_INTERFACE_QXL,
761 .description = "test",
762 .major_version = SPICE_INTERFACE_QXL_MAJOR,
763 .minor_version = SPICE_INTERFACE_QXL_MINOR
764 },
765 .attache_worker = attache_worker,
766 .set_compression_level = set_compression_level,
767 .set_mm_time = set_mm_time,
768 .get_init_info = get_init_info,
769
770 /* the callbacks below are called from spice server thread context */
771 .get_command = get_command,
772 .req_cmd_notification = req_cmd_notification,
773 .release_resource = release_resource,
774 .get_cursor_command = get_cursor_command,
775 .req_cursor_notification = req_cursor_notification,
776 .notify_update = notify_update,
777 .flush_resources = flush_resources,
778 .client_monitors_config = client_monitors_config,
779 .set_client_capabilities = set_client_capabilities,
780};
781
782/* interface for tests */
783void test_add_display_interface(Test* test)
784{
785 spice_server_add_interface(test->server, &test->qxl_instance.base);
786}
787
788static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
789{
790 printf("%s: %d\n", __func__, len);
791 return len;
792}
793
794static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
795{
796 printf("%s: %d\n", __func__, len);
797 return 0;
798}
799
800static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
801{
802 printf("%s: %d\n", __func__, connected);
803}
804
805static SpiceCharDeviceInterface vdagent_sif = {
806 .base.type = SPICE_INTERFACE_CHAR_DEVICE,
807 .base.description = "test spice virtual channel char device",
808 .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
809 .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
810 .state = vmc_state,
811 .write = vmc_write,
812 .read = vmc_read,
813};
814
815SpiceCharDeviceInstance vdagent_sin = {
816 .base = {
817 .sif = &vdagent_sif.base,
818 },
819 .subtype = "vdagent",
820};
821
822void test_add_agent_interface(SpiceServer *server)
823{
824 spice_server_add_interface(server, &vdagent_sin.base);
825}
826
827void test_set_simple_command_list(Test *test, int *simple_commands, int num_commands)
828{
829 int i;
830
831 /* FIXME: leaks */
832 test->commands = malloc(sizeof(*test->commands) * num_commands);
833 memset(test->commands, 0, sizeof(*test->commands) * num_commands);
834 test->num_commands = num_commands;
835 for (i = 0 ; i < num_commands; ++i) {
836 test->commands[i].command = simple_commands[i];
837 }
838}
839
840void test_set_command_list(Test *test, Command *commands, int num_commands)
841{
842 test->commands = commands;
843 test->num_commands = num_commands;
844}
845
846
847Test *test_new(SpiceCoreInterface *core)
848{
849 int port = 5912;
850 Test *test = g_new0(Test, 1);
851 SpiceServer* server = spice_server_new();
852
853 test->qxl_instance.base.sif = &display_sif.base;
854 test->qxl_instance.id = 0;
855
856 test->core = core;
857 test->server = server;
858 test->wakeup_ms = 50;
859 test->cursor_notify = NOTIFY_CURSOR_BATCH;
860 // some common initialization for all display tests
861 printf("TESTER: listening on port %d (unsecure)\n", port);
862 spice_server_set_port(server, port);
863 spice_server_set_noauth(server);
1d7f2da4
DM
864 int res = spice_server_init(server, core);
865 if (res != 0) {
866 g_error("spice_server_init failed, res = %d\n", res);
867 }
cc04455b
DM
868
869 cursor_init();
870 path_init(&path, 0, angle_parts);
871 test->has_secondary = 0;
872 test->wakeup_timer = core->timer_add(do_wakeup, test);
873 return test;
874}
875
876void init_automated()
877{
878 struct sigaction sa;
879
880 memset(&sa, 0, sizeof sa);
881 sa.sa_handler = &sigchld_handler;
882 sigaction(SIGCHLD, &sa, NULL);
883}
884
885__attribute__((noreturn))
886void usage(const char *argv0, const int exitcode)
887{
888#ifdef AUTOMATED_TESTS
889 const char *autoopt=" [--automated-tests]";
890#else
891 const char *autoopt="";
892#endif
893
894 printf("usage: %s%s\n", argv0, autoopt);
895 exit(exitcode);
896}
897
898void spice_test_config_parse_args(int argc, char **argv)
899{
900 struct option options[] = {
901#ifdef AUTOMATED_TESTS
902 {"automated-tests", no_argument, &has_automated_tests, 1},
903#endif
904 {NULL, 0, NULL, 0},
905 };
906 int option_index;
907 int val;
908
909 while ((val = getopt_long(argc, argv, "", options, &option_index)) != -1) {
910 switch (val) {
911 case '?':
912 printf("unrecognized option '%s'\n", argv[optind - 1]);
913 usage(argv[0], EXIT_FAILURE);
914 case 0:
915 break;
916 }
917 }
918
919 if (argc > optind) {
920 printf("unknown argument '%s'\n", argv[optind]);
921 usage(argv[0], EXIT_FAILURE);
922 }
923 if (has_automated_tests) {
924 init_automated();
925 }
926 return;
927}