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