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