spice.c 20.3 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

#include "local_spice.h"
#include "x11spice.h"
38 39
#include "display.h"
#include "session.h"
40
#include "listen.h"
41 42 43 44 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

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;
}

126
static gboolean watch_func(GIOChannel *source, GIOCondition condition, gpointer data)
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
{
    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));
148
    g_source_set_callback(watch->source, (GSourceFunc) watch_func, watch, NULL);
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
    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
176
    g_debug("channel event %d [connection_id %d|type %d|id %d|flags %d]",
177 178
            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
179
        char from[NI_MAXHOST + NI_MAXSERV + 128];
180
        strcpy(from, "Remote");
181
        if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
Jeremy White's avatar
Jeremy White committed
182 183 184
            int rc;
            char host[NI_MAXHOST];
            char server[NI_MAXSERV];
185 186
            rc = getnameinfo((struct sockaddr *) &info->paddr_ext, info->plen_ext, host,
                             sizeof(host), server, sizeof(server), 0);
Jeremy White's avatar
Jeremy White committed
187
            if (rc == 0)
188
                snprintf(from, sizeof(from), "%s:%s", host, server);
Jeremy White's avatar
Jeremy White committed
189 190 191 192 193 194
        }
        session_remote_connected(from);
    }

    if (event == SPICE_CHANNEL_EVENT_DISCONNECTED && info->type == SPICE_CHANNEL_MAIN)
        session_remote_disconnected();
195 196
}

Pavel Grunt's avatar
Pavel Grunt committed
197
static void attach_worker(QXLInstance *qin, QXLWorker *qxl_worker G_GNUC_UNUSED)
198 199 200
{
    static int count = 0;

201 202 203 204 205 206 207 208
    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,
209
    };
210

211
    if (++count > 1) {
212
        g_message("Ignoring worker %d", count);
213 214 215
        return;
    }

216
    spice_qxl_add_memslot(qin, &slot);
217 218 219 220 221
}

static void set_compression_level(QXLInstance *qin, int level)
{
    spice_t *s = SPICE_CONTAINEROF(qin, spice_t, display_sin);
222
    // TODO - set_compression_level is unused
223 224 225
    s->compression_level = level;
}

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

Pavel Grunt's avatar
Pavel Grunt committed
232
static void get_init_info(QXLInstance *qin G_GNUC_UNUSED, QXLDevInitInfo *info)
233 234 235 236 237 238 239
{
    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;
240 241

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

244

245 246
static int get_command(QXLInstance *qin, struct QXLCommandExt *cmd)
{
247 248 249
    spice_t *s = SPICE_CONTAINEROF(qin, spice_t, display_sin);
    QXLDrawable *drawable;

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

    cmd->group_id = 0;
    cmd->flags = 0;
    cmd->cmd.type = QXL_CMD_DRAW;
    cmd->cmd.padding = 0;
    cmd->cmd.data = (QXLPHYSICAL) drawable;

    return 1;
261 262 263 264
}

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

267
    if (session_draw_waiting(s->session) > 0)
268 269
        return 0;

270
    return 1;
271 272
}

Pavel Grunt's avatar
Pavel Grunt committed
273 274
static void release_resource(QXLInstance *qin G_GNUC_UNUSED,
                             struct QXLReleaseInfoExt release_info)
275
{
Jeremy White's avatar
Jeremy White committed
276
    spice_free_release((spice_release_t *) release_info.info->id);
277 278 279 280
}

static int get_cursor_command(QXLInstance *qin, struct QXLCommandExt *cmd)
{
Jeremy White's avatar
Jeremy White committed
281 282 283 284
    spice_t *s = SPICE_CONTAINEROF(qin, spice_t, display_sin);
    struct QXLCursorCmd *cursor;

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

    cmd->group_id = 0;
    cmd->flags = 0;
    cmd->cmd.type = QXL_CMD_CURSOR;
    cmd->cmd.padding = 0;
    cmd->cmd.data = (QXLPHYSICAL) cursor;

    return 1;
295 296 297 298
}

static int req_cursor_notification(QXLInstance *qin)
{
Jeremy White's avatar
Jeremy White committed
299 300 301 302 303
    spice_t *s = SPICE_CONTAINEROF(qin, spice_t, display_sin);

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

304
    return 1;
305 306
}

Pavel Grunt's avatar
Pavel Grunt committed
307
static void notify_update(QXLInstance *qin G_GNUC_UNUSED, uint32_t update_id G_GNUC_UNUSED)
308
{
309
    g_debug("TODO: %s UNIMPLEMENTED", __func__);
310 311
}

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

Pavel Grunt's avatar
Pavel Grunt committed
319
static void async_complete(QXLInstance *qin G_GNUC_UNUSED, uint64_t cookie)
320
{
321 322
    g_debug("%s: cookie 0x%lx", __FUNCTION__, cookie);
    spice_free_release((spice_release_t *) cookie);
323 324
}

Pavel Grunt's avatar
Pavel Grunt committed
325 326 327 328
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)
329
{
330
    g_debug("TODO: %s UNIMPLEMENTED!", __func__);
331 332
}

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

343 344
    g_debug("%s: [num %d|flags 0x%x]", __func__, monitors_config->num_of_monitors,
            monitors_config->flags);
345 346
    for (i = 0; i < monitors_config->num_of_monitors; i++)
        g_debug("  %d:[height %d|width %d|depth %d|x %d|y %d]", i,
347 348 349 350
                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);
351

352
    g_debug("TODO: %s UNIMPLEMENTED", __func__);
353
    return FALSE;
354 355
}

Jeremy White's avatar
Jeremy White committed
356 357 358 359 360 361 362 363
/* 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] = {
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
    [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
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
};

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];
404 405
    }
    else {
Jeremy White's avatar
Jeremy White committed
406 407 408
        frag += MIN_KEYCODE;
    }

409
    session_handle_key(s->session, frag, is_down);
Jeremy White's avatar
Jeremy White committed
410 411 412 413
}

static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
{
414 415 416 417 418 419 420 421 422 423 424
    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
425 426
}

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

432
void tablet_position(SpiceTabletInstance *tablet, int x, int y, uint32_t buttons_state)
433 434
{
    spice_t *s = SPICE_CONTAINEROF(tablet, spice_t, tablet_sin);
435
    session_handle_mouse_position(s->session, x, y, buttons_state);
436 437
}

438
void tablet_wheel(SpiceTabletInstance *tablet, int wheel_motion, uint32_t buttons_state)
439 440
{
    spice_t *s = SPICE_CONTAINEROF(tablet, spice_t, tablet_sin);
441
    session_handle_mouse_wheel(s->session, wheel_motion, buttons_state);
442 443
}

444
void tablet_buttons(SpiceTabletInstance *tablet, uint32_t buttons_state)
445 446
{
    spice_t *s = SPICE_CONTAINEROF(tablet, spice_t, tablet_sin);
447 448 449
    session_handle_mouse_buttons(s->session, buttons_state);
}

450 451 452 453 454
static int send_monitors_config(spice_t *s, int w, int h)
{
    spice_release_t *release;

    QXLMonitorsConfig *monitors = calloc(1, sizeof(QXLMonitorsConfig) + sizeof(QXLHead));
455
    if (!monitors)
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
        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;

    spice_qxl_monitors_config_async(&s->display_sin, (QXLPHYSICAL) monitors, 0, (uint64_t) release);

    return 0;
}

471 472 473 474 475
int spice_create_primary(spice_t *s, int w, int h, int bytes_per_line, void *shmaddr)
{
    QXLDevSurfaceCreate surface;

    memset(&surface, 0, sizeof(surface));
476 477
    surface.height = h;
    surface.width = w;
478

479
    surface.stride = -1 * bytes_per_line;
480 481 482
    surface.type = QXL_SURF_TYPE_PRIMARY;
    surface.flags = 0;
    surface.group_id = 0;
483 484 485
    surface.mouse_mode = TRUE;

    // Position appears to be completely unused
486
    surface.position = 0;
487

488
    /* TODO - compute this dynamically */
489 490
    surface.format = SPICE_SURFACE_FMT_32_xRGB;
    surface.mem = (QXLPHYSICAL) shmaddr;
491

492 493 494
    s->width = w;
    s->height = h;

495 496
    spice_qxl_create_primary_surface(&s->display_sin, 0, &surface);

497
    return send_monitors_config(s, w, h);
498 499 500 501 502
}

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

505 506 507 508 509 510
void initialize_spice_instance(spice_t *s)
{
    static int id = 0;

    static SpiceCoreInterface core = {
        .base = {
511 512 513
                 .major_version = SPICE_INTERFACE_CORE_MAJOR,
                 .minor_version = SPICE_INTERFACE_CORE_MINOR,
                 },
514 515 516 517 518 519 520 521 522 523
        .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
524
    static const QXLInterface display_sif = {
525
        .base = {
526 527 528 529
                 .type = SPICE_INTERFACE_QXL,
                 .description = "x11spice qxl",
                 .major_version = SPICE_INTERFACE_QXL_MAJOR,
                 .minor_version = SPICE_INTERFACE_QXL_MINOR},
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
        .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,
546
        .set_client_capabilities = NULL,    /* Allowed to be unset */
547 548
    };

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

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

569 570 571
    s->core = &core;
    s->display_sin.base.sif = &display_sif.base;
    s->display_sin.id = id++;
Jeremy White's avatar
Jeremy White committed
572 573

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

576 577
}

578 579 580 581 582 583 584
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);
585 586 587

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

588 589
}

590
static int try_listen(spice_t *s, options_t *options)
591 592
{
    int fd;
593 594
    int port;
    char *addr = NULL;
595 596 597
    int start;
    int rc;

598

599
    rc = listen_parse(options->listen, &addr, &start, &port);
600 601
    if (rc)
        return rc;
602

603 604 605
    if (start != -1) {
        fd  = listen_find_open_port(addr, start, port, &port);
        fflush(stdout);
606

607 608
        if (fd >= 0)
            close(fd);
609
        else
610
            return X11SPICE_ERR_AUTO_FAILED;
611
    }
612 613 614 615 616

    if (addr) {
        spice_server_set_addr(s->server, addr, 0);
        free(addr);
    }
Jeremy White's avatar
Jeremy White committed
617 618 619 620 621 622 623 624 625 626 627 628

    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);
    }
629 630

    return 0;
631 632
}

633
int spice_start(spice_t *s, options_t *options, shm_image_t *fullscreen)
634
{
635 636
    int rc;

637 638
    memset(s, 0, sizeof(*s));

639
    s->server = spice_server_new();
640
    if (!s->server)
641 642 643 644
        return X11SPICE_ERR_SPICE_INIT_FAILED;

    initialize_spice_instance(s);

645 646
    set_options(s, options);

647 648 649 650 651
    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
652
            fprintf(stderr, "Error: invalid listen specification '%s'.\n", options->listen);
653
        return rc;
654 655
    }

656
    if (spice_server_init(s->server, s->core) < 0) {
657 658 659 660
        spice_server_destroy(s->server);
        return X11SPICE_ERR_SPICE_INIT_FAILED;
    }

661
    if (spice_server_add_interface(s->server, &s->display_sin.base)) {
662 663 664 665
        spice_server_destroy(s->server);
        return X11SPICE_ERR_SPICE_INIT_FAILED;
    }

666
    if (spice_server_add_interface(s->server, &s->keyboard_sin.base)) {
Jeremy White's avatar
Jeremy White committed
667 668 669 670
        spice_server_destroy(s->server);
        return X11SPICE_ERR_SPICE_INIT_FAILED;
    }

671
    if (spice_server_add_interface(s->server, &s->tablet_sin.base)) {
672 673 674 675
        spice_server_destroy(s->server);
        return X11SPICE_ERR_SPICE_INIT_FAILED;
    }

676 677
    spice_server_vm_start(s->server);

678
    rc = spice_create_primary(s, fullscreen->w, fullscreen->h,
679
                              fullscreen->bytes_per_line, fullscreen->shmaddr);
680 681

    return rc;
682 683 684 685
}

void spice_end(spice_t *s)
{
686 687 688 689
    spice_server_remove_interface(&s->tablet_sin.base);
    spice_server_remove_interface(&s->keyboard_sin.base);
    spice_server_remove_interface(&s->display_sin.base);

690 691
    spice_destroy_primary(s);

692
    spice_server_destroy(s->server);
693

694
}
Jeremy White's avatar
Jeremy White committed
695 696 697 698

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

    return r;
}

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

713
    switch (r->type) {
Jeremy White's avatar
Jeremy White committed
714 715 716 717 718 719 720 721 722 723 724
        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);
}