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