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