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