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