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