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