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