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