spice.c 20.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
    Copyright (C) 2016  Jeremy White <jwhite@codeweavers.com>
    All rights reserved.

    This file is part of x11spice

    x11spice is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    x11spice is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with x11spice.  If not, see <http://www.gnu.org/licenses/>.
*/

21 22 23 24 25 26 27
/*----------------------------------------------------------------------------
**  spice.c
**      This file contains functions that interface with the spice server
**  that we start.  Note that the header file for this file is 'local_spice.h',
**  to avoid name space conflicts.
**--------------------------------------------------------------------------*/

28 29
#include <glib.h>
#include <stdlib.h>
30
#include <stdio.h>
31
#include <string.h>
Jeremy White's avatar
Jeremy White committed
32 33
#include <sys/socket.h>
#include <netdb.h>
34
#include <spice/macros.h>
35

36 37 38 39
/* Obtain definitions for PRIx64 */
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>

40 41
#include "local_spice.h"
#include "x11spice.h"
42 43
#include "display.h"
#include "session.h"
44
#include "listen.h"
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

struct SpiceTimer {
    SpiceTimerFunc func;
    void *opaque;
    GSource *source;
};

static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
{
    SpiceTimer *timer = (SpiceTimer *) calloc(1, sizeof(SpiceTimer));

    timer->func = func;
    timer->opaque = opaque;

    return timer;
}

static gboolean timer_func(gpointer user_data)
{
    SpiceTimer *timer = user_data;

    timer->func(timer->opaque);
    /* timer might be free after func(), don't touch */

    return FALSE;
}

static void timer_cancel(SpiceTimer *timer)
{
    if (timer->source) {
        g_source_destroy(timer->source);
        g_source_unref(timer->source);
        timer->source = NULL;
    }
}

static void timer_start(SpiceTimer *timer, uint32_t ms)
{
    timer_cancel(timer);

    timer->source = g_timeout_source_new(ms);

    g_source_set_callback(timer->source, timer_func, timer, NULL);

    g_source_attach(timer->source, g_main_context_default());

}

static void timer_remove(SpiceTimer *timer)
{
    timer_cancel(timer);
    free(timer);
}

struct SpiceWatch {
    void *opaque;
    GSource *source;
    GIOChannel *channel;
    SpiceWatchFunc func;
};

static GIOCondition spice_event_to_giocondition(int event_mask)
{
    GIOCondition condition = 0;

    if (event_mask & SPICE_WATCH_EVENT_READ)
        condition |= G_IO_IN;
    if (event_mask & SPICE_WATCH_EVENT_WRITE)
        condition |= G_IO_OUT;

    return condition;
}

static int giocondition_to_spice_event(GIOCondition condition)
{
    int event = 0;

    if (condition & G_IO_IN)
        event |= SPICE_WATCH_EVENT_READ;
    if (condition & G_IO_OUT)
        event |= SPICE_WATCH_EVENT_WRITE;

    return event;
}

130
static gboolean watch_func(GIOChannel *source, GIOCondition condition, gpointer data)
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
{
    SpiceWatch *watch = data;
    int fd = g_io_channel_unix_get_fd(source);

    watch->func(fd, giocondition_to_spice_event(condition), watch->opaque);

    return TRUE;
}

static void watch_update_mask(SpiceWatch *watch, int event_mask)
{
    if (watch->source) {
        g_source_destroy(watch->source);
        g_source_unref(watch->source);
        watch->source = NULL;
    }

    if (!event_mask)
        return;

    watch->source = g_io_create_watch(watch->channel, spice_event_to_giocondition(event_mask));
152
    g_source_set_callback(watch->source, (GSourceFunc) watch_func, watch, NULL);
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
    g_source_attach(watch->source, g_main_context_default());
}

static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque)
{
    SpiceWatch *watch;

    watch = calloc(1, sizeof(SpiceWatch));
    watch->channel = g_io_channel_unix_new(fd);
    watch->func = func;
    watch->opaque = opaque;

    watch_update_mask(watch, event_mask);

    return watch;
}

static void watch_remove(SpiceWatch *watch)
{
    watch_update_mask(watch, 0);

    g_io_channel_unref(watch->channel);
    free(watch);
}

static void channel_event(int event, SpiceChannelEventInfo *info)
{
Jeremy White's avatar
Jeremy White committed
180
    g_debug("channel event %d [connection_id %d|type %d|id %d|flags %d]",
181 182
            event, info->connection_id, info->type, info->id, info->flags);
    if (event == SPICE_CHANNEL_EVENT_INITIALIZED && info->type == SPICE_CHANNEL_MAIN) {
Jeremy White's avatar
Jeremy White committed
183
        char from[NI_MAXHOST + NI_MAXSERV + 128];
184
        strcpy(from, "Remote");
185
        if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
Jeremy White's avatar
Jeremy White committed
186 187 188
            int rc;
            char host[NI_MAXHOST];
            char server[NI_MAXSERV];
189 190
            rc = getnameinfo((struct sockaddr *) &info->paddr_ext, info->plen_ext, host,
                             sizeof(host), server, sizeof(server), 0);
Jeremy White's avatar
Jeremy White committed
191
            if (rc == 0)
192
                snprintf(from, sizeof(from), "%s:%s", host, server);
Jeremy White's avatar
Jeremy White committed
193 194 195 196 197 198
        }
        session_remote_connected(from);
    }

    if (event == SPICE_CHANNEL_EVENT_DISCONNECTED && info->type == SPICE_CHANNEL_MAIN)
        session_remote_disconnected();
199 200
}

Pavel Grunt's avatar
Pavel Grunt committed
201
static void attach_worker(QXLInstance *qin, QXLWorker *qxl_worker G_GNUC_UNUSED)
202 203 204
{
    static int count = 0;

205 206 207 208 209 210 211 212
    static QXLDevMemSlot slot = {
        .slot_group_id = 0,
        .slot_id = 0,
        .generation = 0,
        .virt_start = 0,
        .virt_end = ~0,
        .addr_delta = 0,
        .qxl_ram_size = ~0,
213
    };
214

215
    if (++count > 1) {
216
        g_message("Ignoring worker %d", count);
217 218 219
        return;
    }

220
    spice_qxl_add_memslot(qin, &slot);
221 222 223 224 225
}

static void set_compression_level(QXLInstance *qin, int level)
{
    spice_t *s = SPICE_CONTAINEROF(qin, spice_t, display_sin);
226
    // TODO - set_compression_level is unused
227 228 229
    s->compression_level = level;
}

230 231
/* Newer spice servers no longer transmit this information,
 * so let's just disregard it */
Pavel Grunt's avatar
Pavel Grunt committed
232
static void set_mm_time(QXLInstance *qin G_GNUC_UNUSED, uint32_t mm_time G_GNUC_UNUSED)
233 234 235
{
}

Pavel Grunt's avatar
Pavel Grunt committed
236
static void get_init_info(QXLInstance *qin G_GNUC_UNUSED, QXLDevInitInfo *info)
237 238 239 240 241 242 243
{
    memset(info, 0, sizeof(*info));
    info->num_memslots = 1;
    info->num_memslots_groups = 1;
    info->memslot_id_bits = 1;
    info->memslot_gen_bits = 1;
    info->n_surfaces = 1;
244 245

    /* TODO - it would be useful to think through surface count a bit here */
246 247
}

248

249 250
static int get_command(QXLInstance *qin, struct QXLCommandExt *cmd)
{
251 252 253
    spice_t *s = SPICE_CONTAINEROF(qin, spice_t, display_sin);
    QXLDrawable *drawable;

254
    drawable = session_pop_draw(s->session);
255
    if (!drawable)
256 257 258 259 260 261
        return 0;

    cmd->group_id = 0;
    cmd->flags = 0;
    cmd->cmd.type = QXL_CMD_DRAW;
    cmd->cmd.padding = 0;
262
    cmd->cmd.data = (uintptr_t) drawable;
263 264

    return 1;
265 266 267 268
}

static int req_cmd_notification(QXLInstance *qin)
{
269 270
    spice_t *s = SPICE_CONTAINEROF(qin, spice_t, display_sin);

271
    if (session_draw_waiting(s->session) > 0)
272 273
        return 0;

274
    return 1;
275 276
}

Pavel Grunt's avatar
Pavel Grunt committed
277 278
static void release_resource(QXLInstance *qin G_GNUC_UNUSED,
                             struct QXLReleaseInfoExt release_info)
279
{
280
    spice_free_release((spice_release_t *) (uintptr_t) release_info.info->id);
281 282 283 284
}

static int get_cursor_command(QXLInstance *qin, struct QXLCommandExt *cmd)
{
Jeremy White's avatar
Jeremy White committed
285 286 287 288
    spice_t *s = SPICE_CONTAINEROF(qin, spice_t, display_sin);
    struct QXLCursorCmd *cursor;

    cursor = session_pop_cursor(s->session);
289
    if (!cursor)
Jeremy White's avatar
Jeremy White committed
290 291 292 293 294 295
        return 0;

    cmd->group_id = 0;
    cmd->flags = 0;
    cmd->cmd.type = QXL_CMD_CURSOR;
    cmd->cmd.padding = 0;
296
    cmd->cmd.data = (uintptr_t) cursor;
Jeremy White's avatar
Jeremy White committed
297 298

    return 1;
299 300 301 302
}

static int req_cursor_notification(QXLInstance *qin)
{
Jeremy White's avatar
Jeremy White committed
303 304 305 306 307
    spice_t *s = SPICE_CONTAINEROF(qin, spice_t, display_sin);

    if (session_cursor_waiting(s->session) > 0)
        return 0;

308
    return 1;
309 310
}

Pavel Grunt's avatar
Pavel Grunt committed
311
static void notify_update(QXLInstance *qin G_GNUC_UNUSED, uint32_t update_id G_GNUC_UNUSED)
312
{
313
    g_debug("TODO: %s UNIMPLEMENTED", __func__);
314 315
}

Pavel Grunt's avatar
Pavel Grunt committed
316
static int flush_resources(QXLInstance *qin G_GNUC_UNUSED)
317
{
318
    g_debug("TODO: %s UNIMPLEMENTEDs", __func__);
319 320
    // Return 0 to direct the server to flush resources
    return 1;
321 322
}

Pavel Grunt's avatar
Pavel Grunt committed
323
static void async_complete(QXLInstance *qin G_GNUC_UNUSED, uint64_t cookie)
324
{
325 326
    g_debug("%s: cookie %#" PRIx64, __FUNCTION__, cookie);
    spice_free_release((spice_release_t *) (uintptr_t) cookie);
327 328
}

Pavel Grunt's avatar
Pavel Grunt committed
329 330 331 332
static void update_area_complete(QXLInstance *qin G_GNUC_UNUSED,
                                 uint32_t surface_id G_GNUC_UNUSED,
                                 struct QXLRect *updated_rects G_GNUC_UNUSED,
                                 uint32_t num_updated_rects G_GNUC_UNUSED)
333
{
334
    g_debug("TODO: %s UNIMPLEMENTED!", __func__);
335 336
}

Pavel Grunt's avatar
Pavel Grunt committed
337 338
static int client_monitors_config(QXLInstance *qin G_GNUC_UNUSED,
                                  VDAgentMonitorsConfig *monitors_config)
339
{
Pavel Grunt's avatar
Pavel Grunt committed
340
    uint i;
341
    if (!monitors_config) {
342 343 344 345 346
        /* a NULL is used as a test to see if we support this function */
        g_debug("%s: NULL monitors_config", __func__);
        return TRUE;
    }

347 348
    g_debug("%s: [num %d|flags 0x%x]", __func__, monitors_config->num_of_monitors,
            monitors_config->flags);
349 350
    for (i = 0; i < monitors_config->num_of_monitors; i++)
        g_debug("  %d:[height %d|width %d|depth %d|x %d|y %d]", i,
351 352 353 354
                monitors_config->monitors[i].height,
                monitors_config->monitors[i].width,
                monitors_config->monitors[i].depth,
                monitors_config->monitors[i].x, monitors_config->monitors[i].y);
355

356
    g_debug("TODO: %s UNIMPLEMENTED", __func__);
357
    return FALSE;
358 359
}

Jeremy White's avatar
Jeremy White committed
360 361 362 363 364 365 366 367
/* spice sends AT scancodes (with a strange escape).
 * But xf86PostKeyboardEvent expects scancodes. Apparently most of the time
 * you just need to add MIN_KEYCODE, see xf86-input-keyboard/src/atKeynames
 * and xf86-input-keyboard/src/kbd.c:PostKbdEvent:
 *   xf86PostKeyboardEvent(device, scanCode + MIN_KEYCODE, down); */
#define MIN_KEYCODE     8

static uint8_t escaped_map[256] = {
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
    [0x1c] = 104,               //KEY_KP_Enter,
    [0x1d] = 105,               //KEY_RCtrl,
    [0x2a] = 0,                 //KEY_LMeta, // REDKEY_FAKE_L_SHIFT
    [0x35] = 106,               //KEY_KP_Divide,
    [0x36] = 0,                 //KEY_RMeta, // REDKEY_FAKE_R_SHIFT
    [0x37] = 107,               //KEY_Print,
    [0x38] = 108,               //KEY_AltLang,
    [0x46] = 127,               //KEY_Break,
    [0x47] = 110,               //KEY_Home,
    [0x48] = 111,               //KEY_Up,
    [0x49] = 112,               //KEY_PgUp,
    [0x4b] = 113,               //KEY_Left,
    [0x4d] = 114,               //KEY_Right,
    [0x4f] = 115,               //KEY_End,
    [0x50] = 116,               //KEY_Down,
    [0x51] = 117,               //KEY_PgDown,
    [0x52] = 118,               //KEY_Insert,
    [0x53] = 119,               //KEY_Delete,
    [0x5b] = 133,               //0, // REDKEY_LEFT_CMD,
    [0x5c] = 134,               //0, // REDKEY_RIGHT_CMD,
    [0x5d] = 135,               //KEY_Menu,
Jeremy White's avatar
Jeremy White committed
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
};

static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
{
    spice_t *s = SPICE_CONTAINEROF(sin, spice_t, keyboard_sin);
    int is_down;

    if (frag == 224) {
        s->escape = frag;
        return;
    }
    is_down = frag & 0x80 ? FALSE : TRUE;
    frag = frag & 0x7f;
    if (s->escape == 224) {
        s->escape = 0;
        if (escaped_map[frag] == 0) {
            g_warning("spiceqxl_inputs.c: kbd_push_key: escaped_map[%d] == 0", frag);
        }
        frag = escaped_map[frag];
408 409
    }
    else {
Jeremy White's avatar
Jeremy White committed
410 411 412
        frag += MIN_KEYCODE;
    }

413
    session_handle_key(s->session, frag, is_down);
Jeremy White's avatar
Jeremy White committed
414 415 416 417
}

static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
{
418 419 420 421 422 423 424 425 426 427 428
    spice_t *s = SPICE_CONTAINEROF(sin, spice_t, keyboard_sin);
    uint8_t ret = 0;

    if (session_get_one_led(s->session, "Caps Lock"))
        ret |= SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK;
    if (session_get_one_led(s->session, "Scroll Lock"))
        ret |= SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK;
    if (session_get_one_led(s->session, "Num Lock"))
        ret |= SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK;

    return ret;
Jeremy White's avatar
Jeremy White committed
429 430
}

Pavel Grunt's avatar
Pavel Grunt committed
431
void tablet_set_logical_size(SpiceTabletInstance *tablet G_GNUC_UNUSED, int width, int height)
432
{
433
    g_debug("TODO: %s UNIMPLEMENTED. (width %dx%d)", __func__, width, height);
434 435
}

436
void tablet_position(SpiceTabletInstance *tablet, int x, int y, uint32_t buttons_state)
437 438
{
    spice_t *s = SPICE_CONTAINEROF(tablet, spice_t, tablet_sin);
439
    session_handle_mouse_position(s->session, x, y, buttons_state);
440 441
}

442
void tablet_wheel(SpiceTabletInstance *tablet, int wheel_motion, uint32_t buttons_state)
443 444
{
    spice_t *s = SPICE_CONTAINEROF(tablet, spice_t, tablet_sin);
445
    session_handle_mouse_wheel(s->session, wheel_motion, buttons_state);
446 447
}

448
void tablet_buttons(SpiceTabletInstance *tablet, uint32_t buttons_state)
449 450
{
    spice_t *s = SPICE_CONTAINEROF(tablet, spice_t, tablet_sin);
451 452 453
    session_handle_mouse_buttons(s->session, buttons_state);
}

454 455 456 457 458
static int send_monitors_config(spice_t *s, int w, int h)
{
    spice_release_t *release;

    QXLMonitorsConfig *monitors = calloc(1, sizeof(QXLMonitorsConfig) + sizeof(QXLHead));
459
    if (!monitors)
460 461 462 463 464 465 466 467 468 469
        return X11SPICE_ERR_MALLOC;
    release = spice_create_release(s, RELEASE_MEMORY, monitors);

    monitors->count = 1;
    monitors->max_allowed = 1;
    monitors->heads[0].id = 0;
    monitors->heads[0].surface_id = 0;
    monitors->heads[0].width = w;
    monitors->heads[0].height = h;

470
    spice_qxl_monitors_config_async(&s->display_sin, (uintptr_t) monitors, 0, (uintptr_t) release);
471 472 473 474

    return 0;
}

475 476 477 478 479
int spice_create_primary(spice_t *s, int w, int h, int bytes_per_line, void *shmaddr)
{
    QXLDevSurfaceCreate surface;

    memset(&surface, 0, sizeof(surface));
480 481
    surface.height = h;
    surface.width = w;
482

483
    surface.stride = -1 * bytes_per_line;
484 485 486
    surface.type = QXL_SURF_TYPE_PRIMARY;
    surface.flags = 0;
    surface.group_id = 0;
487 488 489
    surface.mouse_mode = TRUE;

    // Position appears to be completely unused
490
    surface.position = 0;
491

492
    /* TODO - compute this dynamically */
493
    surface.format = SPICE_SURFACE_FMT_32_xRGB;
494
    surface.mem = (uintptr_t) shmaddr;
495

496 497 498
    s->width = w;
    s->height = h;

499 500
    spice_qxl_create_primary_surface(&s->display_sin, 0, &surface);

501
    return send_monitors_config(s, w, h);
502 503 504 505 506
}

void spice_destroy_primary(spice_t *s)
{
    spice_qxl_destroy_primary_surface(&s->display_sin, 0);
507 508
}

509 510 511 512 513 514
void initialize_spice_instance(spice_t *s)
{
    static int id = 0;

    static SpiceCoreInterface core = {
        .base = {
515 516 517
                 .major_version = SPICE_INTERFACE_CORE_MAJOR,
                 .minor_version = SPICE_INTERFACE_CORE_MINOR,
                 },
518 519 520 521 522 523 524 525 526 527
        .timer_add = timer_add,
        .timer_start = timer_start,
        .timer_cancel = timer_cancel,
        .timer_remove = timer_remove,
        .watch_add = watch_add,
        .watch_update_mask = watch_update_mask,
        .watch_remove = watch_remove,
        .channel_event = channel_event
    };

Pavel Grunt's avatar
Pavel Grunt committed
528
    static const QXLInterface display_sif = {
529
        .base = {
530 531 532 533
                 .type = SPICE_INTERFACE_QXL,
                 .description = "x11spice qxl",
                 .major_version = SPICE_INTERFACE_QXL_MAJOR,
                 .minor_version = SPICE_INTERFACE_QXL_MINOR},
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
        .attache_worker = attach_worker,
        .set_compression_level = set_compression_level,
        .set_mm_time = set_mm_time,
        .get_init_info = get_init_info,

        /* the callbacks below are called from spice server thread context */
        .get_command = get_command,
        .req_cmd_notification = req_cmd_notification,
        .release_resource = release_resource,
        .get_cursor_command = get_cursor_command,
        .req_cursor_notification = req_cursor_notification,
        .notify_update = notify_update,
        .flush_resources = flush_resources,
        .async_complete = async_complete,
        .update_area_complete = update_area_complete,
        .client_monitors_config = client_monitors_config,
550
        .set_client_capabilities = NULL,    /* Allowed to be unset */
551 552
    };

Jeremy White's avatar
Jeremy White committed
553
    static const SpiceKbdInterface keyboard_sif = {
554 555
        .base.type = SPICE_INTERFACE_KEYBOARD,
        .base.description = "x11spice keyboard",
Jeremy White's avatar
Jeremy White committed
556 557
        .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR,
        .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR,
558 559
        .push_scan_freg = kbd_push_key,
        .get_leds = kbd_get_leds,
Jeremy White's avatar
Jeremy White committed
560 561
    };

562
    static const SpiceTabletInterface tablet_sif = {
563 564
        .base.type = SPICE_INTERFACE_TABLET,
        .base.description = "x11spice tablet",
565 566
        .base.major_version = SPICE_INTERFACE_TABLET_MAJOR,
        .base.minor_version = SPICE_INTERFACE_TABLET_MINOR,
567 568 569 570
        .set_logical_size = tablet_set_logical_size,
        .position = tablet_position,
        .wheel = tablet_wheel,
        .buttons = tablet_buttons,
571 572
    };

573 574 575
    s->core = &core;
    s->display_sin.base.sif = &display_sif.base;
    s->display_sin.id = id++;
Jeremy White's avatar
Jeremy White committed
576 577

    s->keyboard_sin.base.sif = &keyboard_sif.base;
578
    s->tablet_sin.base.sif = &tablet_sif.base;
Jeremy White's avatar
Jeremy White committed
579

580 581
}

582 583 584 585 586 587 588
static void set_options(spice_t *s, options_t *options)
{
    if (options->disable_ticketing)
        spice_server_set_noauth(s->server);

    if (options->spice_password)
        spice_server_set_ticket(s->server, options->spice_password, 0, 0, 0);
589 590 591

    spice_server_set_exit_on_disconnect(s->server, options->exit_on_disconnect);

592 593
}

594
static int try_listen(spice_t *s, options_t *options)
595 596
{
    int fd;
597 598
    int port;
    char *addr = NULL;
599 600 601
    int start;
    int rc;

602

603
    rc = listen_parse(options->listen, &addr, &start, &port);
604 605
    if (rc)
        return rc;
606

607 608 609
    if (start != -1) {
        fd  = listen_find_open_port(addr, start, port, &port);
        fflush(stdout);
610

611 612
        if (fd >= 0)
            close(fd);
613
        else
614
            return X11SPICE_ERR_AUTO_FAILED;
615
    }
616 617 618 619 620

    if (addr) {
        spice_server_set_addr(s->server, addr, 0);
        free(addr);
    }
Jeremy White's avatar
Jeremy White committed
621 622 623 624 625 626 627 628 629 630 631 632

    if (options->ssl.enabled) {
        spice_server_set_tls(s->server, port,
                            options->ssl.ca_cert_file,
                            options->ssl.certs_file,
                            options->ssl.private_key_file,
                            options->ssl.key_password,
                            options->ssl.dh_key_file,
                            options->ssl.ciphersuite);
    } else {
        spice_server_set_port(s->server, port);
    }
633 634

    return 0;
635 636
}

637
int spice_start(spice_t *s, options_t *options, shm_image_t *fullscreen)
638
{
639 640
    int rc;

641 642
    memset(s, 0, sizeof(*s));

643
    s->server = spice_server_new();
644
    if (!s->server)
645 646 647 648
        return X11SPICE_ERR_SPICE_INIT_FAILED;

    initialize_spice_instance(s);

649 650
    set_options(s, options);

651 652 653 654 655
    rc = try_listen(s, options);
    if (rc) {
        if (rc == X11SPICE_ERR_AUTO_FAILED)
            fprintf(stderr, "Error: unable to open any port in range '%s'.\n", options->listen);
        else
656
            fprintf(stderr, "Error: invalid listen specification '%s'.\n", options->listen);
657
        return rc;
658 659
    }

660
    if (spice_server_init(s->server, s->core) < 0) {
661 662 663 664
        spice_server_destroy(s->server);
        return X11SPICE_ERR_SPICE_INIT_FAILED;
    }

665
    if (spice_server_add_interface(s->server, &s->display_sin.base)) {
666 667 668 669
        spice_server_destroy(s->server);
        return X11SPICE_ERR_SPICE_INIT_FAILED;
    }

670
    if (spice_server_add_interface(s->server, &s->keyboard_sin.base)) {
Jeremy White's avatar
Jeremy White committed
671 672 673 674
        spice_server_destroy(s->server);
        return X11SPICE_ERR_SPICE_INIT_FAILED;
    }

675
    if (spice_server_add_interface(s->server, &s->tablet_sin.base)) {
676 677 678 679
        spice_server_destroy(s->server);
        return X11SPICE_ERR_SPICE_INIT_FAILED;
    }

680 681
    spice_server_vm_start(s->server);

682
    rc = spice_create_primary(s, fullscreen->w, fullscreen->h,
683
                              fullscreen->bytes_per_line, fullscreen->shmaddr);
684 685

    return rc;
686 687 688 689
}

void spice_end(spice_t *s)
{
690 691 692
    spice_server_remove_interface(&s->tablet_sin.base);
    spice_server_remove_interface(&s->keyboard_sin.base);

693
    spice_destroy_primary(s);
694
    spice_server_remove_interface(&s->display_sin.base);
695

696
    spice_server_destroy(s->server);
697

698
}
Jeremy White's avatar
Jeremy White committed
699 700 701 702

spice_release_t *spice_create_release(spice_t *s, release_type_t type, void *data)
{
    spice_release_t *r = malloc(sizeof(*r));
703
    if (r) {
Jeremy White's avatar
Jeremy White committed
704 705 706 707 708 709 710 711 712 713 714 715 716
        r->s = s;
        r->type = type;
        r->data = data;
    }

    return r;
}

void spice_free_release(spice_release_t *r)
{
    if (!r)
        return;

717
    switch (r->type) {
Jeremy White's avatar
Jeremy White committed
718 719 720 721 722 723 724 725 726 727 728
        case RELEASE_SHMI:
            destroy_shm_image(&r->s->session->display, (shm_image_t *) r->data);
            break;

        case RELEASE_MEMORY:
            free(r->data);
            break;
    }

    free(r);
}