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