]> git.proxmox.com Git - spiceterm.git/blob - screen.c
b0b4444a0a284e80f7607ab722e2207f404ab48b
[spiceterm.git] / screen.c
1 /*
2
3 Copyright (C) 2013 Proxmox Server Solutions GmbH
4
5 Copyright: spiceterm is under GNU GPL, the GNU General Public License.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; version 2 dated June, 1991.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20
21 Author: Dietmar Maurer <dietmar@proxmox.com>
22
23 Note: qlx drawing code is copied from spice-server test code.
24
25 */
26
27 #include <stdlib.h>
28 #include <math.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #include <wait.h>
34 #include <sys/select.h>
35 #include <sys/types.h>
36 #include <getopt.h>
37
38 #include <spice.h>
39 #include <spice/enums.h>
40 #include <spice/macros.h>
41 #include <spice/qxl_dev.h>
42
43 #include "glyphs.h"
44
45 #include "spiceterm.h"
46
47 static int debug = 0;
48
49 #define DPRINTF(x, format, ...) { \
50 if (x <= debug) { \
51 printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \
52 } \
53 }
54
55 #define MEM_SLOT_GROUP_ID 0
56
57 #define NOTIFY_DISPLAY_BATCH (SINGLE_PART/2)
58 #define NOTIFY_CURSOR_BATCH 10
59
60 /* these colours are from linux kernel drivers/char/vt.c */
61 /* the default colour table, for VGA+ colour systems */
62 int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa,
63 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff};
64 int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa,
65 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff};
66 int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa,
67 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff};
68
69 /* Parts cribbed from spice-display.h/.c/qxl.c */
70
71 typedef struct SimpleSpiceUpdate {
72 QXLCommandExt ext; // first
73 QXLDrawable drawable;
74 QXLImage image;
75 uint8_t *bitmap;
76 } SimpleSpiceUpdate;
77
78 static void
79 spice_screen_destroy_update(SimpleSpiceUpdate *update)
80 {
81 if (!update) {
82 return;
83 }
84 if (update->drawable.clip.type != SPICE_CLIP_TYPE_NONE) {
85 uint8_t *ptr = (uint8_t*)update->drawable.clip.data;
86 free(ptr);
87 }
88 g_free(update->bitmap);
89 g_free(update);
90 }
91
92 #define DEFAULT_WIDTH 640
93 #define DEFAULT_HEIGHT 320
94
95 static int unique = 1;
96
97 static void
98 set_cmd(QXLCommandExt *ext, uint32_t type, QXLPHYSICAL data)
99 {
100 ext->cmd.type = type;
101 ext->cmd.data = data;
102 ext->cmd.padding = 0;
103 ext->group_id = MEM_SLOT_GROUP_ID;
104 ext->flags = 0;
105 }
106
107 static void
108 simple_set_release_info(QXLReleaseInfo *info, intptr_t ptr)
109 {
110 info->id = ptr;
111 //info->group_id = MEM_SLOT_GROUP_ID;
112 }
113
114 /* Note: push_command/get_command are called from different threads */
115
116 static void
117 push_command(SpiceScreen *spice_screen, QXLCommandExt *ext)
118 {
119 g_mutex_lock(spice_screen->command_mutex);
120
121 while (spice_screen->commands_end - spice_screen->commands_start >= COMMANDS_SIZE) {
122 g_cond_wait(spice_screen->command_cond, spice_screen->command_mutex);
123 }
124
125 g_assert(spice_screen->commands_end - spice_screen->commands_start < COMMANDS_SIZE);
126
127 spice_screen->commands[spice_screen->commands_end % COMMANDS_SIZE] = ext;
128 spice_screen->commands_end++;
129
130 g_mutex_unlock(spice_screen->command_mutex);
131
132 spice_screen->qxl_worker->wakeup(spice_screen->qxl_worker);
133 }
134
135 /* bitmap are freed, so they must be allocated with g_malloc */
136 static SimpleSpiceUpdate *
137 spice_screen_update_from_bitmap_cmd(uint32_t surface_id, QXLRect bbox, uint8_t *bitmap)
138 {
139 SimpleSpiceUpdate *update;
140 QXLDrawable *drawable;
141 QXLImage *image;
142 uint32_t bw, bh;
143
144 bh = bbox.bottom - bbox.top;
145 bw = bbox.right - bbox.left;
146
147 update = g_new0(SimpleSpiceUpdate, 1);
148 update->bitmap = bitmap;
149 drawable = &update->drawable;
150 image = &update->image;
151
152 drawable->surface_id = surface_id;
153
154 drawable->bbox = bbox;
155 drawable->clip.type = SPICE_CLIP_TYPE_NONE;
156 drawable->effect = QXL_EFFECT_OPAQUE;
157 simple_set_release_info(&drawable->release_info, (intptr_t)update);
158 drawable->type = QXL_DRAW_COPY;
159 drawable->surfaces_dest[0] = -1;
160 drawable->surfaces_dest[1] = -1;
161 drawable->surfaces_dest[2] = -1;
162
163 drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT;
164 drawable->u.copy.src_bitmap = (intptr_t)image;
165 drawable->u.copy.src_area.right = bw;
166 drawable->u.copy.src_area.bottom = bh;
167
168 QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, unique);
169 image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
170 image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN;
171 image->bitmap.stride = bw * 4;
172 image->descriptor.width = image->bitmap.x = bw;
173 image->descriptor.height = image->bitmap.y = bh;
174 image->bitmap.data = (intptr_t)bitmap;
175 image->bitmap.palette = 0;
176 image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
177
178 set_cmd(&update->ext, QXL_CMD_DRAW, (intptr_t)drawable);
179
180 return update;
181 }
182
183 static SimpleSpiceUpdate *
184 spice_screen_draw_char_cmd(SpiceScreen *spice_screen, int x, int y, int c,
185 int fg, int bg)
186 {
187 int top, left;
188 uint8_t *dst;
189 uint8_t *bitmap;
190 int bw, bh;
191 int i, j;
192 QXLRect bbox;
193
194 left = x*8;
195 top = y*16;
196
197 unique++;
198
199 bw = 8;
200 bh = 16;
201
202 bitmap = dst = g_malloc(bw * bh * 4);
203
204 unsigned char *data = vt_font_data + c*16;
205 unsigned char d = *data;
206
207 g_assert(fg >= 0 && fg < 16);
208 g_assert(bg >= 0 && bg < 16);
209
210 unsigned char fgc_red = default_red[fg];
211 unsigned char fgc_blue = default_blu[fg];
212 unsigned char fgc_green = default_grn[fg];
213 unsigned char bgc_red = default_red[bg];
214 unsigned char bgc_blue = default_blu[bg];
215 unsigned char bgc_green = default_grn[bg];
216
217 for (j = 0; j < 16; j++) {
218 for (i = 0; i < 8; i++) {
219 if ((i&7) == 0) {
220 d=*data;
221 data++;
222 }
223 if (d&0x80) {
224 *(dst) = fgc_blue;
225 *(dst+1) = fgc_green;
226 *(dst+2) = fgc_red;
227 *(dst+3) = 0;
228 } else {
229 *(dst) = bgc_blue;
230 *(dst+1) = bgc_green;
231 *(dst+2) = bgc_red;
232 *(dst+3) = 0;
233 }
234 d<<=1;
235 dst += 4;
236 }
237 }
238
239 bbox.left = left; bbox.top = top;
240 bbox.right = left + bw; bbox.bottom = top + bh;
241
242 return spice_screen_update_from_bitmap_cmd(0, bbox, bitmap);
243 }
244
245 void
246 spice_screen_scroll(SpiceScreen *spice_screen, int x1, int y1,
247 int x2, int y2, int src_x, int src_y)
248 {
249 SimpleSpiceUpdate *update;
250 QXLDrawable *drawable;
251 QXLRect bbox;
252
253 int surface_id = 0;
254
255 update = g_new0(SimpleSpiceUpdate, 1);
256 drawable = &update->drawable;
257
258 bbox.left = x1;
259 bbox.top = y1;
260 bbox.right = x2;
261 bbox.bottom = y2;
262
263 drawable->surface_id = surface_id;
264
265 drawable->bbox = bbox;
266 drawable->clip.type = SPICE_CLIP_TYPE_NONE;
267 drawable->effect = QXL_EFFECT_OPAQUE;
268 simple_set_release_info(&drawable->release_info, (intptr_t)update);
269 drawable->type = QXL_COPY_BITS;
270 drawable->surfaces_dest[0] = -1;
271 drawable->surfaces_dest[1] = -1;
272 drawable->surfaces_dest[2] = -1;
273
274 drawable->u.copy_bits.src_pos.x = src_x;
275 drawable->u.copy_bits.src_pos.y = src_y;
276
277 set_cmd(&update->ext, QXL_CMD_DRAW, (intptr_t)drawable);
278
279 push_command(spice_screen, &update->ext);
280 }
281
282 void
283 spice_screen_clear(SpiceScreen *spice_screen, int x1, int y1, int x2, int y2)
284 {
285 SimpleSpiceUpdate *update;
286 QXLDrawable *drawable;
287 QXLRect bbox;
288
289 int surface_id = 0;
290
291 update = g_new0(SimpleSpiceUpdate, 1);
292 drawable = &update->drawable;
293
294 bbox.left = x1;
295 bbox.top = y1;
296 bbox.right = x2;
297 bbox.bottom = y2;
298
299 drawable->surface_id = surface_id;
300
301 drawable->bbox = bbox;
302 drawable->clip.type = SPICE_CLIP_TYPE_NONE;
303 drawable->effect = QXL_EFFECT_OPAQUE;
304 simple_set_release_info(&drawable->release_info, (intptr_t)update);
305 drawable->type = QXL_DRAW_BLACKNESS;
306 drawable->surfaces_dest[0] = -1;
307 drawable->surfaces_dest[1] = -1;
308 drawable->surfaces_dest[2] = -1;
309
310 set_cmd(&update->ext, QXL_CMD_DRAW, (intptr_t)drawable);
311
312 push_command(spice_screen, &update->ext);
313 }
314
315 static void
316 create_primary_surface(SpiceScreen *spice_screen, uint32_t width,
317 uint32_t height)
318 {
319 QXLWorker *qxl_worker = spice_screen->qxl_worker;
320 QXLDevSurfaceCreate surface = { 0, };
321
322 g_assert(height <= MAX_HEIGHT);
323 g_assert(width <= MAX_WIDTH);
324 g_assert(height > 0);
325 g_assert(width > 0);
326
327 surface.format = SPICE_SURFACE_FMT_32_xRGB;
328 surface.width = spice_screen->primary_width = width;
329 surface.height = spice_screen->primary_height = height;
330 surface.stride = -width * 4; /* negative? */
331 surface.mouse_mode = TRUE; /* unused by red_worker */
332 surface.flags = 0;
333 surface.type = 0; /* unused by red_worker */
334 surface.position = 0; /* unused by red_worker */
335 surface.mem = (uint64_t)&spice_screen->primary_surface;
336 surface.group_id = MEM_SLOT_GROUP_ID;
337
338 spice_screen->width = width;
339 spice_screen->height = height;
340
341 qxl_worker->create_primary_surface(qxl_worker, 0, &surface);
342 }
343
344 QXLDevMemSlot slot = {
345 .slot_group_id = MEM_SLOT_GROUP_ID,
346 .slot_id = 0,
347 .generation = 0,
348 .virt_start = 0,
349 .virt_end = ~0,
350 .addr_delta = 0,
351 .qxl_ram_size = ~0,
352 };
353
354 static void
355 attache_worker(QXLInstance *qin, QXLWorker *_qxl_worker)
356 {
357 SpiceScreen *spice_screen = SPICE_CONTAINEROF(qin, SpiceScreen, qxl_instance);
358
359 if (spice_screen->qxl_worker) {
360 g_assert_not_reached();
361 }
362
363 spice_screen->qxl_worker = _qxl_worker;
364 spice_screen->qxl_worker->add_memslot(spice_screen->qxl_worker, &slot);
365 create_primary_surface(spice_screen, DEFAULT_WIDTH, DEFAULT_HEIGHT);
366 spice_screen->qxl_worker->start(spice_screen->qxl_worker);
367 }
368
369 static void
370 set_compression_level(QXLInstance *qin, int level)
371 {
372 /* not used */
373 }
374
375 static void
376 set_mm_time(QXLInstance *qin, uint32_t mm_time)
377 {
378 /* not used */
379 }
380
381 static void
382 get_init_info(QXLInstance *qin, QXLDevInitInfo *info)
383 {
384 memset(info, 0, sizeof(*info));
385 info->num_memslots = 1;
386 info->num_memslots_groups = 1;
387 info->memslot_id_bits = 1;
388 info->memslot_gen_bits = 1;
389 info->n_surfaces = 1;
390 }
391
392 /* called from spice_server thread (i.e. red_worker thread) */
393 static int
394 get_command(QXLInstance *qin, struct QXLCommandExt *ext)
395 {
396 SpiceScreen *spice_screen = SPICE_CONTAINEROF(qin, SpiceScreen, qxl_instance);
397 int res = FALSE;
398
399 g_mutex_lock(spice_screen->command_mutex);
400
401 if ((spice_screen->commands_end - spice_screen->commands_start) == 0) {
402 res = FALSE;
403 goto ret;
404 }
405
406 *ext = *spice_screen->commands[spice_screen->commands_start % COMMANDS_SIZE];
407 g_assert(spice_screen->commands_start < spice_screen->commands_end);
408 spice_screen->commands_start++;
409 g_cond_signal(spice_screen->command_cond);
410
411 res = TRUE;
412
413 ret:
414 g_mutex_unlock(spice_screen->command_mutex);
415 return res;
416 }
417
418 static int
419 req_cmd_notification(QXLInstance *qin)
420 {
421 //SpiceScreen *spice_screen = SPICE_CONTAINEROF(qin, SpiceScreen, qxl_instance);
422 //spice_screen->core->timer_start(spice_screen->wakeup_timer, spice_screen->wakeup_ms);
423
424 return TRUE;
425 }
426
427 static void
428 release_resource(QXLInstance *qin, struct QXLReleaseInfoExt release_info)
429 {
430 QXLCommandExt *ext = (QXLCommandExt*)(unsigned long)release_info.info->id;
431
432 g_assert(release_info.group_id == MEM_SLOT_GROUP_ID);
433 switch (ext->cmd.type) {
434 case QXL_CMD_DRAW:
435 spice_screen_destroy_update((void*)ext);
436 break;
437 case QXL_CMD_SURFACE:
438 free(ext);
439 break;
440 case QXL_CMD_CURSOR: {
441 QXLCursorCmd *cmd = (QXLCursorCmd *)(unsigned long)ext->cmd.data;
442 if (cmd->type == QXL_CURSOR_SET) {
443 free(cmd);
444 }
445 free(ext);
446 break;
447 }
448 default:
449 abort();
450 }
451 }
452
453 #define CURSOR_WIDTH 8
454 #define CURSOR_HEIGHT 16
455
456 static struct {
457 QXLCursor cursor;
458 uint8_t data[CURSOR_WIDTH * CURSOR_HEIGHT * 4]; // 32bit per pixel
459 } cursor;
460
461 static void
462 cursor_init()
463 {
464 cursor.cursor.header.unique = 0;
465 cursor.cursor.header.type = SPICE_CURSOR_TYPE_COLOR32;
466 cursor.cursor.header.width = CURSOR_WIDTH;
467 cursor.cursor.header.height = CURSOR_HEIGHT;
468 cursor.cursor.header.hot_spot_x = 0;
469 cursor.cursor.header.hot_spot_y = 0;
470 cursor.cursor.data_size = CURSOR_WIDTH * CURSOR_HEIGHT * 4;
471
472 // X drivers addes it to the cursor size because it could be
473 // cursor data information or another cursor related stuffs.
474 // Otherwise, the code will break in client/cursor.cpp side,
475 // that expect the data_size plus cursor information.
476 // Blame cursor protocol for this. :-)
477 cursor.cursor.data_size += 128;
478 cursor.cursor.chunk.data_size = cursor.cursor.data_size;
479 cursor.cursor.chunk.prev_chunk = cursor.cursor.chunk.next_chunk = 0;
480 }
481
482 static int
483 get_cursor_command(QXLInstance *qin, struct QXLCommandExt *ext)
484 {
485 SpiceScreen *spice_screen = SPICE_CONTAINEROF(qin, SpiceScreen, qxl_instance);
486 static int set = 1;
487 static int x = 0, y = 0;
488 QXLCursorCmd *cursor_cmd;
489 QXLCommandExt *cmd;
490
491 if (!spice_screen->cursor_notify) {
492 return FALSE;
493 }
494
495 spice_screen->cursor_notify--;
496 cmd = calloc(sizeof(QXLCommandExt), 1);
497 cursor_cmd = calloc(sizeof(QXLCursorCmd), 1);
498
499 cursor_cmd->release_info.id = (unsigned long)cmd;
500
501 if (set) {
502 cursor_cmd->type = QXL_CURSOR_SET;
503 cursor_cmd->u.set.position.x = 0;
504 cursor_cmd->u.set.position.y = 0;
505 cursor_cmd->u.set.visible = TRUE;
506 cursor_cmd->u.set.shape = (unsigned long)&cursor;
507 // white rect as cursor
508 memset(cursor.data, 255, sizeof(cursor.data));
509 set = 0;
510 } else {
511 cursor_cmd->type = QXL_CURSOR_MOVE;
512 cursor_cmd->u.position.x = x++ % spice_screen->primary_width;
513 cursor_cmd->u.position.y = y++ % spice_screen->primary_height;
514 }
515
516 cmd->cmd.data = (unsigned long)cursor_cmd;
517 cmd->cmd.type = QXL_CMD_CURSOR;
518 cmd->group_id = MEM_SLOT_GROUP_ID;
519 cmd->flags = 0;
520 *ext = *cmd;
521
522 return TRUE;
523 }
524
525 static int
526 req_cursor_notification(QXLInstance *qin)
527 {
528 /* not used */
529
530 return TRUE;
531 }
532
533 static void
534 notify_update(QXLInstance *qin, uint32_t update_id)
535 {
536 /* not used */
537 }
538
539 static int
540 flush_resources(QXLInstance *qin)
541 {
542 /* not used */
543
544 return TRUE;
545 }
546
547 static int
548 client_monitors_config(QXLInstance *qin, VDAgentMonitorsConfig *monitors_config)
549 {
550 /* not used */
551
552 return 0;
553 }
554
555 static void
556 set_client_capabilities(QXLInstance *qin, uint8_t client_present,
557 uint8_t caps[58])
558 {
559 SpiceScreen *spice_screen = SPICE_CONTAINEROF(qin, SpiceScreen, qxl_instance);
560
561 DPRINTF(1, "%s: present %d caps %d", __func__, client_present, caps[0]);
562
563 if (spice_screen->on_client_connected && client_present) {
564 spice_screen->on_client_connected(spice_screen);
565 }
566 if (spice_screen->on_client_disconnected && !client_present) {
567 spice_screen->on_client_disconnected(spice_screen);
568 }
569 }
570
571 static int client_count = 0;
572
573 static void
574 client_connected(SpiceScreen *spice_screen)
575 {
576 client_count++;
577
578 DPRINTF(1, "%s: client_count = %d", __func__, client_count);
579 }
580
581 static void
582 client_disconnected(SpiceScreen *spice_screen)
583 {
584 if (client_count > 0) {
585 client_count--;
586 DPRINTF(1, "%s: client_count = %d", __func__, client_count);
587 exit(0); // fixme: cleanup?
588 }
589 }
590
591 static void
592 do_conn_timeout(void *opaque)
593 {
594 // SpiceScreen *spice_screen = opaque;
595
596 if (client_count <= 0) {
597 printf("connection timeout - stopping server\n");
598 exit (0); // fixme: cleanup?
599 }
600 }
601
602 QXLInterface display_sif = {
603 .base = {
604 .type = SPICE_INTERFACE_QXL,
605 .description = "spiceterm display server",
606 .major_version = SPICE_INTERFACE_QXL_MAJOR,
607 .minor_version = SPICE_INTERFACE_QXL_MINOR
608 },
609 .attache_worker = attache_worker,
610 .set_compression_level = set_compression_level,
611 .set_mm_time = set_mm_time,
612 .get_init_info = get_init_info,
613
614 /* the callbacks below are called from spice server thread context */
615 .get_command = get_command,
616 .req_cmd_notification = req_cmd_notification,
617 .release_resource = release_resource,
618 .get_cursor_command = get_cursor_command,
619 .req_cursor_notification = req_cursor_notification,
620 .notify_update = notify_update,
621 .flush_resources = flush_resources,
622 .client_monitors_config = client_monitors_config,
623 .set_client_capabilities = set_client_capabilities,
624 };
625
626 void
627 spice_screen_add_display_interface(SpiceScreen* spice_screen)
628 {
629 spice_server_add_interface(spice_screen->server, &spice_screen->qxl_instance.base);
630 }
631
632 /* vdagent interface - not sure why we need that? */
633 static int
634 vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
635 {
636 return len;
637 }
638
639 static int
640 vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
641 {
642 return 0;
643 }
644
645 static void
646 vmc_state(SpiceCharDeviceInstance *sin, int connected)
647 {
648
649 }
650
651 static SpiceCharDeviceInterface vdagent_sif = {
652 .base.type = SPICE_INTERFACE_CHAR_DEVICE,
653 .base.description = "spice virtual channel char device",
654 .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
655 .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
656 .state = vmc_state,
657 .write = vmc_write,
658 .read = vmc_read,
659 };
660
661 SpiceCharDeviceInstance vdagent_sin = {
662 .base = {
663 .sif = &vdagent_sif.base,
664 },
665 .subtype = "vdagent",
666 };
667
668 void
669 spice_screen_add_agent_interface(SpiceServer *server)
670 {
671 spice_server_add_interface(server, &vdagent_sin.base);
672 }
673
674 void
675 spice_screen_draw_char(SpiceScreen *spice_screen, int x, int y, gunichar2 ch, TextAttributes attrib)
676 {
677 int fg, bg;
678
679 if (attrib.invers) {
680 bg = attrib.fgcol;
681 fg = attrib.bgcol;
682 } else {
683 bg = attrib.bgcol;
684 fg = attrib.fgcol;
685 }
686
687 if (attrib.bold) {
688 fg += 8;
689 }
690
691 // unsuported attributes = (attrib.blink || attrib.unvisible)
692
693 // fixme:
694 //if (attrib.uline) {
695 //rfbDrawLine (vt->screen, rx, ry + 14, rxe, ry + 14, fg);
696 //}
697
698 int c = vt_fontmap[ch];
699
700 SimpleSpiceUpdate *update;
701 update = spice_screen_draw_char_cmd(spice_screen, x, y, c, fg, bg);
702 push_command(spice_screen, &update->ext);
703 }
704
705 SpiceScreen *
706 spice_screen_new(SpiceCoreInterface *core)
707 {
708 int port = 5912;
709 SpiceScreen *spice_screen = g_new0(SpiceScreen, 1);
710 SpiceServer* server = spice_server_new();
711
712 spice_screen->command_cond = g_cond_new();
713 spice_screen->command_mutex = g_mutex_new();
714
715 spice_screen->on_client_connected = client_connected,
716 spice_screen->on_client_disconnected = client_disconnected,
717
718 spice_screen->qxl_instance.base.sif = &display_sif.base;
719 spice_screen->qxl_instance.id = 0;
720
721 spice_screen->core = core;
722 spice_screen->server = server;
723
724 spice_screen->cursor_notify = NOTIFY_CURSOR_BATCH;
725
726 printf("listening on port %d (unsecure)\n", port);
727
728 spice_server_set_port(server, port);
729 spice_server_set_noauth(server);
730
731 int res = spice_server_init(server, core);
732 if (res != 0) {
733 g_error("spice_server_init failed, res = %d\n", res);
734 }
735
736 cursor_init();
737
738 int timeout = 10; // max time to wait for client connection
739 spice_screen->conn_timeout_timer = core->timer_add(do_conn_timeout, spice_screen);
740 spice_screen->core->timer_start(spice_screen->conn_timeout_timer, timeout*1000);
741
742 return spice_screen;
743 }