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