]> git.proxmox.com Git - spiceterm.git/blame - test_display_base.c
remove unnecessary code
[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"
cc04455b
DM
17
18#define MEM_SLOT_GROUP_ID 0
19
20#define NOTIFY_DISPLAY_BATCH (SINGLE_PART/2)
21#define NOTIFY_CURSOR_BATCH 10
22
23/* Parts cribbed from spice-display.h/.c/qxl.c */
24
25typedef struct SimpleSpiceUpdate {
26 QXLCommandExt ext; // first
27 QXLDrawable drawable;
28 QXLImage image;
29 uint8_t *bitmap;
30} SimpleSpiceUpdate;
31
cc04455b
DM
32static void test_spice_destroy_update(SimpleSpiceUpdate *update)
33{
34 if (!update) {
35 return;
36 }
37 if (update->drawable.clip.type != SPICE_CLIP_TYPE_NONE) {
38 uint8_t *ptr = (uint8_t*)update->drawable.clip.data;
39 free(ptr);
40 }
41 free(update->bitmap);
42 free(update);
43}
44
45#define DEFAULT_WIDTH 640
46#define DEFAULT_HEIGHT 320
47
48#define SINGLE_PART 4
49static const int angle_parts = 64 / SINGLE_PART;
50static int unique = 1;
51static int color = -1;
52static int c_i = 0;
53
cc04455b
DM
54__attribute__((noreturn))
55static void sigchld_handler(int signal_num) // wait for the child process and exit
56{
57 int status;
58 wait(&status);
59 exit(0);
60}
61
cc04455b
DM
62static void set_cmd(QXLCommandExt *ext, uint32_t type, QXLPHYSICAL data)
63{
64 ext->cmd.type = type;
65 ext->cmd.data = data;
66 ext->cmd.padding = 0;
67 ext->group_id = MEM_SLOT_GROUP_ID;
68 ext->flags = 0;
69}
70
71static void simple_set_release_info(QXLReleaseInfo *info, intptr_t ptr)
72{
73 info->id = ptr;
74 //info->group_id = MEM_SLOT_GROUP_ID;
75}
76
cc04455b
DM
77/* bitmap and rects are freed, so they must be allocated with malloc */
78SimpleSpiceUpdate *test_spice_create_update_from_bitmap(uint32_t surface_id,
79 QXLRect bbox,
80 uint8_t *bitmap,
81 uint32_t num_clip_rects,
82 QXLRect *clip_rects)
83{
84 SimpleSpiceUpdate *update;
85 QXLDrawable *drawable;
86 QXLImage *image;
87 uint32_t bw, bh;
88
89 bh = bbox.bottom - bbox.top;
90 bw = bbox.right - bbox.left;
91
92 update = calloc(sizeof(*update), 1);
93 update->bitmap = bitmap;
94 drawable = &update->drawable;
95 image = &update->image;
96
97 drawable->surface_id = surface_id;
98
99 drawable->bbox = bbox;
100 if (num_clip_rects == 0) {
101 drawable->clip.type = SPICE_CLIP_TYPE_NONE;
102 } else {
103 QXLClipRects *cmd_clip;
104
105 cmd_clip = calloc(sizeof(QXLClipRects) + num_clip_rects*sizeof(QXLRect), 1);
106 cmd_clip->num_rects = num_clip_rects;
107 cmd_clip->chunk.data_size = num_clip_rects*sizeof(QXLRect);
108 cmd_clip->chunk.prev_chunk = cmd_clip->chunk.next_chunk = 0;
109 memcpy(cmd_clip + 1, clip_rects, cmd_clip->chunk.data_size);
110
111 drawable->clip.type = SPICE_CLIP_TYPE_RECTS;
112 drawable->clip.data = (intptr_t)cmd_clip;
113
114 free(clip_rects);
115 }
116 drawable->effect = QXL_EFFECT_OPAQUE;
117 simple_set_release_info(&drawable->release_info, (intptr_t)update);
118 drawable->type = QXL_DRAW_COPY;
119 drawable->surfaces_dest[0] = -1;
120 drawable->surfaces_dest[1] = -1;
121 drawable->surfaces_dest[2] = -1;
122
123 drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT;
124 drawable->u.copy.src_bitmap = (intptr_t)image;
125 drawable->u.copy.src_area.right = bw;
126 drawable->u.copy.src_area.bottom = bh;
127
128 QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, unique);
129 image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
130 image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN;
131 image->bitmap.stride = bw * 4;
132 image->descriptor.width = image->bitmap.x = bw;
133 image->descriptor.height = image->bitmap.y = bh;
134 image->bitmap.data = (intptr_t)bitmap;
135 image->bitmap.palette = 0;
136 image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
137
138 set_cmd(&update->ext, QXL_CMD_DRAW, (intptr_t)drawable);
139
140 return update;
141}
142
26c64941 143static SimpleSpiceUpdate *test_spice_create_update_draw(Test *test, uint32_t surface_id)
cc04455b
DM
144{
145 int top, left;
146 uint8_t *dst;
147 uint8_t *bitmap;
148 int bw, bh;
149 int i;
150 QXLRect bbox;
151
26c64941
DM
152 left = 100;
153 top = 100;
154
cc04455b
DM
155
156 if (surface_id != 0) {
157 color = (color + 1) % 2;
158 } else {
159 color = surface_id;
160 }
161
26c64941
DM
162 c_i ++;
163
cc04455b
DM
164 unique++;
165
26c64941
DM
166 bw = 8;
167 bh = 16;
cc04455b
DM
168
169 bitmap = dst = malloc(bw * bh * 4);
170 //printf("allocated %p\n", dst);
171
172 for (i = 0 ; i < bh * bw ; ++i, dst+=4) {
173 *dst = (color+i % 255);
174 *(dst+((1+c_i)%3)) = 255 - color;
175 *(dst+((2+c_i)%3)) = (color * (color + i)) & 0xff;
176 *(dst+((3+c_i)%3)) = 0;
177 }
178
179 bbox.left = left; bbox.top = top;
180 bbox.right = left + bw; bbox.bottom = top + bh;
181 return test_spice_create_update_from_bitmap(surface_id, bbox, bitmap, 0, NULL);
182}
183
cc04455b 184
cc04455b
DM
185static void create_primary_surface(Test *test, uint32_t width,
186 uint32_t height)
187{
188 QXLWorker *qxl_worker = test->qxl_worker;
189 QXLDevSurfaceCreate surface = { 0, };
190
191 g_assert(height <= MAX_HEIGHT);
192 g_assert(width <= MAX_WIDTH);
193 g_assert(height > 0);
194 g_assert(width > 0);
195
196 surface.format = SPICE_SURFACE_FMT_32_xRGB;
197 surface.width = test->primary_width = width;
198 surface.height = test->primary_height = height;
199 surface.stride = -width * 4; /* negative? */
200 surface.mouse_mode = TRUE; /* unused by red_worker */
201 surface.flags = 0;
202 surface.type = 0; /* unused by red_worker */
203 surface.position = 0; /* unused by red_worker */
204 surface.mem = (uint64_t)&test->primary_surface;
205 surface.group_id = MEM_SLOT_GROUP_ID;
206
207 test->width = width;
208 test->height = height;
209
210 qxl_worker->create_primary_surface(qxl_worker, 0, &surface);
211}
212
213QXLDevMemSlot slot = {
214.slot_group_id = MEM_SLOT_GROUP_ID,
215.slot_id = 0,
216.generation = 0,
217.virt_start = 0,
218.virt_end = ~0,
219.addr_delta = 0,
220.qxl_ram_size = ~0,
221};
222
223static void attache_worker(QXLInstance *qin, QXLWorker *_qxl_worker)
224{
225 Test *test = SPICE_CONTAINEROF(qin, Test, qxl_instance);
226
227 if (test->qxl_worker) {
228 if (test->qxl_worker != _qxl_worker)
229 printf("%s ignored, %p is set, ignoring new %p\n", __func__,
230 test->qxl_worker, _qxl_worker);
231 else
232 printf("%s ignored, redundant\n", __func__);
233 return;
234 }
235 printf("%s\n", __func__);
236 test->qxl_worker = _qxl_worker;
237 test->qxl_worker->add_memslot(test->qxl_worker, &slot);
238 create_primary_surface(test, DEFAULT_WIDTH, DEFAULT_HEIGHT);
239 test->qxl_worker->start(test->qxl_worker);
240}
241
242static void set_compression_level(QXLInstance *qin, int level)
243{
244 printf("%s\n", __func__);
245}
246
247static void set_mm_time(QXLInstance *qin, uint32_t mm_time)
248{
249}
cc04455b
DM
250static void get_init_info(QXLInstance *qin, QXLDevInitInfo *info)
251{
252 memset(info, 0, sizeof(*info));
253 info->num_memslots = 1;
254 info->num_memslots_groups = 1;
255 info->memslot_id_bits = 1;
256 info->memslot_gen_bits = 1;
aae93060 257 info->n_surfaces = 1;
cc04455b
DM
258}
259
aae93060 260
cc04455b
DM
261// We shall now have a ring of commands, so that we can update
262// it from a separate thread - since get_command is called from
263// the worker thread, and we need to sometimes do an update_area,
264// which cannot be done from red_worker context (not via dispatcher,
265// since you get a deadlock, and it isn't designed to be done
266// any other way, so no point testing that).
267int commands_end = 0;
268int commands_start = 0;
269struct QXLCommandExt* commands[1024];
270
271#define COMMANDS_SIZE COUNT(commands)
272
273static void push_command(QXLCommandExt *ext)
274{
275 g_assert(commands_end - commands_start < COMMANDS_SIZE);
276 commands[commands_end % COMMANDS_SIZE] = ext;
277 commands_end++;
278}
279
280static struct QXLCommandExt *get_simple_command(void)
281{
282 struct QXLCommandExt *ret = commands[commands_start % COMMANDS_SIZE];
283 g_assert(commands_start < commands_end);
284 commands_start++;
285 return ret;
286}
287
288static int get_num_commands(void)
289{
290 return commands_end - commands_start;
291}
292
293// called from spice_server thread (i.e. red_worker thread)
294static int get_command(QXLInstance *qin, struct QXLCommandExt *ext)
295{
296 if (get_num_commands() == 0) {
297 return FALSE;
298 }
299 *ext = *get_simple_command();
300 return TRUE;
301}
302
303static void produce_command(Test *test)
304{
305 Command *command;
306 QXLWorker *qxl_worker = test->qxl_worker;
307
308 g_assert(qxl_worker);
309
cc04455b
DM
310 if (!test->num_commands) {
311 usleep(1000);
312 return;
313 }
314
315 command = &test->commands[test->cmd_index];
316 if (command->cb) {
317 command->cb(test, command);
318 }
319 switch (command->command) {
cc04455b 320 /* Drawing commands, they all push a command to the command ring */
cc04455b
DM
321 case SIMPLE_DRAW: {
322 SimpleSpiceUpdate *update;
26c64941 323 update = test_spice_create_update_draw(test, 0);
cc04455b
DM
324 push_command(&update->ext);
325 break;
326 }
cc04455b
DM
327 }
328 test->cmd_index = (test->cmd_index + 1) % test->num_commands;
329}
330
331static int req_cmd_notification(QXLInstance *qin)
332{
333 Test *test = SPICE_CONTAINEROF(qin, Test, qxl_instance);
334
335 test->core->timer_start(test->wakeup_timer, test->wakeup_ms);
336 return TRUE;
337}
338
339static void do_wakeup(void *opaque)
340{
341 Test *test = opaque;
342 int notify;
343
344 test->cursor_notify = NOTIFY_CURSOR_BATCH;
345 for (notify = NOTIFY_DISPLAY_BATCH; notify > 0;--notify) {
346 produce_command(test);
347 }
348
349 test->core->timer_start(test->wakeup_timer, test->wakeup_ms);
350 test->qxl_worker->wakeup(test->qxl_worker);
351}
352
353static void release_resource(QXLInstance *qin, struct QXLReleaseInfoExt release_info)
354{
355 QXLCommandExt *ext = (QXLCommandExt*)(unsigned long)release_info.info->id;
356 //printf("%s\n", __func__);
357 g_assert(release_info.group_id == MEM_SLOT_GROUP_ID);
358 switch (ext->cmd.type) {
359 case QXL_CMD_DRAW:
360 test_spice_destroy_update((void*)ext);
361 break;
362 case QXL_CMD_SURFACE:
363 free(ext);
364 break;
365 case QXL_CMD_CURSOR: {
366 QXLCursorCmd *cmd = (QXLCursorCmd *)(unsigned long)ext->cmd.data;
367 if (cmd->type == QXL_CURSOR_SET) {
368 free(cmd);
369 }
370 free(ext);
371 break;
372 }
373 default:
374 abort();
375 }
376}
377
378#define CURSOR_WIDTH 32
379#define CURSOR_HEIGHT 32
380
381static struct {
382 QXLCursor cursor;
383 uint8_t data[CURSOR_WIDTH * CURSOR_HEIGHT * 4]; // 32bit per pixel
384} cursor;
385
386static void cursor_init()
387{
388 cursor.cursor.header.unique = 0;
389 cursor.cursor.header.type = SPICE_CURSOR_TYPE_COLOR32;
390 cursor.cursor.header.width = CURSOR_WIDTH;
391 cursor.cursor.header.height = CURSOR_HEIGHT;
392 cursor.cursor.header.hot_spot_x = 0;
393 cursor.cursor.header.hot_spot_y = 0;
394 cursor.cursor.data_size = CURSOR_WIDTH * CURSOR_HEIGHT * 4;
395
396 // X drivers addes it to the cursor size because it could be
397 // cursor data information or another cursor related stuffs.
398 // Otherwise, the code will break in client/cursor.cpp side,
399 // that expect the data_size plus cursor information.
400 // Blame cursor protocol for this. :-)
401 cursor.cursor.data_size += 128;
402 cursor.cursor.chunk.data_size = cursor.cursor.data_size;
403 cursor.cursor.chunk.prev_chunk = cursor.cursor.chunk.next_chunk = 0;
404}
405
406static int get_cursor_command(QXLInstance *qin, struct QXLCommandExt *ext)
407{
408 Test *test = SPICE_CONTAINEROF(qin, Test, qxl_instance);
409 static int color = 0;
410 static int set = 1;
411 static int x = 0, y = 0;
412 QXLCursorCmd *cursor_cmd;
413 QXLCommandExt *cmd;
414
415 if (!test->cursor_notify) {
416 return FALSE;
417 }
418
419 test->cursor_notify--;
420 cmd = calloc(sizeof(QXLCommandExt), 1);
421 cursor_cmd = calloc(sizeof(QXLCursorCmd), 1);
422
423 cursor_cmd->release_info.id = (unsigned long)cmd;
424
425 if (set) {
426 cursor_cmd->type = QXL_CURSOR_SET;
427 cursor_cmd->u.set.position.x = 0;
428 cursor_cmd->u.set.position.y = 0;
429 cursor_cmd->u.set.visible = TRUE;
430 cursor_cmd->u.set.shape = (unsigned long)&cursor;
431 // Only a white rect (32x32) as cursor
432 memset(cursor.data, 255, sizeof(cursor.data));
433 set = 0;
434 } else {
435 cursor_cmd->type = QXL_CURSOR_MOVE;
436 cursor_cmd->u.position.x = x++ % test->primary_width;
437 cursor_cmd->u.position.y = y++ % test->primary_height;
438 }
439
440 cmd->cmd.data = (unsigned long)cursor_cmd;
441 cmd->cmd.type = QXL_CMD_CURSOR;
442 cmd->group_id = MEM_SLOT_GROUP_ID;
443 cmd->flags = 0;
444 *ext = *cmd;
445 //printf("%s\n", __func__);
446 return TRUE;
447}
448
449static int req_cursor_notification(QXLInstance *qin)
450{
451 printf("%s\n", __func__);
452 return TRUE;
453}
454
455static void notify_update(QXLInstance *qin, uint32_t update_id)
456{
457 printf("%s\n", __func__);
458}
459
460static int flush_resources(QXLInstance *qin)
461{
462 printf("%s\n", __func__);
463 return TRUE;
464}
465
466static int client_monitors_config(QXLInstance *qin,
467 VDAgentMonitorsConfig *monitors_config)
468{
469 if (!monitors_config) {
470 printf("%s: NULL monitors_config\n", __func__);
471 } else {
472 printf("%s: %d\n", __func__, monitors_config->num_of_monitors);
473 }
474 return 0;
475}
476
477static void set_client_capabilities(QXLInstance *qin,
478 uint8_t client_present,
479 uint8_t caps[58])
480{
481 Test *test = SPICE_CONTAINEROF(qin, Test, qxl_instance);
482
483 printf("%s: present %d caps %d\n", __func__, client_present, caps[0]);
484 if (test->on_client_connected && client_present) {
485 test->on_client_connected(test);
486 }
487 if (test->on_client_disconnected && !client_present) {
488 test->on_client_disconnected(test);
489 }
490}
491
492QXLInterface display_sif = {
493 .base = {
494 .type = SPICE_INTERFACE_QXL,
495 .description = "test",
496 .major_version = SPICE_INTERFACE_QXL_MAJOR,
497 .minor_version = SPICE_INTERFACE_QXL_MINOR
498 },
499 .attache_worker = attache_worker,
500 .set_compression_level = set_compression_level,
501 .set_mm_time = set_mm_time,
502 .get_init_info = get_init_info,
503
504 /* the callbacks below are called from spice server thread context */
505 .get_command = get_command,
506 .req_cmd_notification = req_cmd_notification,
507 .release_resource = release_resource,
508 .get_cursor_command = get_cursor_command,
509 .req_cursor_notification = req_cursor_notification,
510 .notify_update = notify_update,
511 .flush_resources = flush_resources,
512 .client_monitors_config = client_monitors_config,
513 .set_client_capabilities = set_client_capabilities,
514};
515
516/* interface for tests */
517void test_add_display_interface(Test* test)
518{
519 spice_server_add_interface(test->server, &test->qxl_instance.base);
520}
521
522static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
523{
524 printf("%s: %d\n", __func__, len);
525 return len;
526}
527
528static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
529{
530 printf("%s: %d\n", __func__, len);
531 return 0;
532}
533
534static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
535{
536 printf("%s: %d\n", __func__, connected);
537}
538
539static SpiceCharDeviceInterface vdagent_sif = {
540 .base.type = SPICE_INTERFACE_CHAR_DEVICE,
541 .base.description = "test spice virtual channel char device",
542 .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
543 .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
544 .state = vmc_state,
545 .write = vmc_write,
546 .read = vmc_read,
547};
548
549SpiceCharDeviceInstance vdagent_sin = {
550 .base = {
551 .sif = &vdagent_sif.base,
552 },
553 .subtype = "vdagent",
554};
555
556void test_add_agent_interface(SpiceServer *server)
557{
558 spice_server_add_interface(server, &vdagent_sin.base);
559}
560
561void test_set_simple_command_list(Test *test, int *simple_commands, int num_commands)
562{
563 int i;
564
565 /* FIXME: leaks */
566 test->commands = malloc(sizeof(*test->commands) * num_commands);
567 memset(test->commands, 0, sizeof(*test->commands) * num_commands);
568 test->num_commands = num_commands;
569 for (i = 0 ; i < num_commands; ++i) {
570 test->commands[i].command = simple_commands[i];
571 }
572}
573
574void test_set_command_list(Test *test, Command *commands, int num_commands)
575{
576 test->commands = commands;
577 test->num_commands = num_commands;
578}
579
580
581Test *test_new(SpiceCoreInterface *core)
582{
583 int port = 5912;
584 Test *test = g_new0(Test, 1);
585 SpiceServer* server = spice_server_new();
586
587 test->qxl_instance.base.sif = &display_sif.base;
588 test->qxl_instance.id = 0;
589
590 test->core = core;
591 test->server = server;
592 test->wakeup_ms = 50;
593 test->cursor_notify = NOTIFY_CURSOR_BATCH;
594 // some common initialization for all display tests
595 printf("TESTER: listening on port %d (unsecure)\n", port);
596 spice_server_set_port(server, port);
597 spice_server_set_noauth(server);
1d7f2da4
DM
598 int res = spice_server_init(server, core);
599 if (res != 0) {
600 g_error("spice_server_init failed, res = %d\n", res);
601 }
cc04455b
DM
602
603 cursor_init();
cc04455b
DM
604 test->has_secondary = 0;
605 test->wakeup_timer = core->timer_add(do_wakeup, test);
606 return test;
607}
608
cc04455b
DM
609
610__attribute__((noreturn))
611void usage(const char *argv0, const int exitcode)
612{
cc04455b 613
7f30e760 614 printf("usage: %s\n", argv0);
cc04455b
DM
615 exit(exitcode);
616}
617
618void spice_test_config_parse_args(int argc, char **argv)
619{
620 struct option options[] = {
7f30e760 621// {"automated-tests", no_argument, &has_automated_tests, 1},
cc04455b
DM
622 {NULL, 0, NULL, 0},
623 };
624 int option_index;
625 int val;
626
627 while ((val = getopt_long(argc, argv, "", options, &option_index)) != -1) {
628 switch (val) {
629 case '?':
630 printf("unrecognized option '%s'\n", argv[optind - 1]);
631 usage(argv[0], EXIT_FAILURE);
632 case 0:
633 break;
634 }
635 }
636
637 if (argc > optind) {
638 printf("unknown argument '%s'\n", argv[optind]);
639 usage(argv[0], EXIT_FAILURE);
640 }
cc04455b
DM
641 return;
642}