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