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