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