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