[Spice-devel] [PATCH 10/22] server: rename files

Fabiano FidĂȘncio fidencio at redhat.com
Wed Dec 2 12:31:39 PST 2015


On Wed, Dec 2, 2015 at 5:19 PM, Frediano Ziglio <fziglio at redhat.com> wrote:
> ---
>  server/Makefile.am                        |   56 +-
>  server/char-device.c                      | 1035 ++++++++++++++++++
>  server/char-device.h                      |  216 ++++
>  server/char_device.c                      | 1035 ------------------
>  server/char_device.h                      |  216 ----
>  server/dcc-encoders.h                     |   10 +-
>  server/display-channel.h                  |   14 +-
>  server/glz-encoder-dict.c                 |  633 +++++++++++
>  server/glz-encoder-dict.h                 |   69 ++
>  server/glz-encoder-priv.h                 |  186 ++++
>  server/glz-encoder.c                      |  311 ++++++
>  server/glz-encoder.h                      |   55 +
>  server/glz_encoder.c                      |  311 ------
>  server/glz_encoder.h                      |   55 -
>  server/glz_encoder_dictionary.c           |  633 -----------
>  server/glz_encoder_dictionary.h           |   69 --
>  server/glz_encoder_dictionary_protected.h |  186 ----
>  server/image-cache.c                      |  214 ++++
>  server/image-cache.h                      |   65 ++
>  server/inputs-channel.c                   |  679 ++++++++++++
>  server/inputs-channel.h                   |   38 +
>  server/inputs_channel.c                   |  679 ------------
>  server/inputs_channel.h                   |   38 -
>  server/jpeg-encoder.c                     |  248 +++++
>  server/jpeg-encoder.h                     |   61 ++
>  server/jpeg_encoder.c                     |  248 -----
>  server/jpeg_encoder.h                     |   61 --
>  server/main-channel.c                     | 1345 ++++++++++++++++++++++++
>  server/main-channel.h                     |  103 ++
>  server/main-dispatcher.c                  |  217 ++++
>  server/main-dispatcher.h                  |   36 +
>  server/main_channel.c                     | 1345 ------------------------
>  server/main_channel.h                     |  103 --
>  server/main_dispatcher.c                  |  217 ----
>  server/main_dispatcher.h                  |   36 -
>  server/memslot.c                          |  184 ++++
>  server/memslot.h                          |   72 ++
>  server/migration-protocol.h               |  213 ++++
>  server/migration_protocol.h               |  213 ----
>  server/mjpeg-encoder.c                    | 1375 ++++++++++++++++++++++++
>  server/mjpeg-encoder.h                    |  100 ++
>  server/mjpeg_encoder.c                    | 1375 ------------------------
>  server/mjpeg_encoder.h                    |  100 --
>  server/red_channel.c                      |    2 +-
>  server/red_dispatcher.c                   |    2 +-
>  server/red_memslots.c                     |  184 ----
>  server/red_memslots.h                     |   72 --
>  server/red_parse_qxl.c                    |    2 +-
>  server/red_parse_qxl.h                    |    2 +-
>  server/red_record_qxl.c                   |    4 +-
>  server/red_record_qxl.h                   |    2 +-
>  server/red_replay_qxl.c                   |    2 +-
>  server/reds.c                             |   12 +-
>  server/reds.h                             |    4 +-
>  server/reds_stream.c                      |    2 +-
>  server/reds_sw_canvas.c                   |   26 -
>  server/reds_sw_canvas.h                   |   24 -
>  server/smartcard.c                        |    4 +-
>  server/snd_worker.c                       | 1625 -----------------------------
>  server/snd_worker.h                       |   33 -
>  server/sound.c                            | 1625 +++++++++++++++++++++++++++++
>  server/sound.h                            |   33 +
>  server/spice_image_cache.c                |  214 ----
>  server/spice_image_cache.h                |   65 --
>  server/spicevmc.c                         |    4 +-
>  server/stream.h                           |    4 +-
>  server/sw-canvas.c                        |   26 +
>  server/sw-canvas.h                        |   24 +
>  server/zlib-encoder.c                     |  125 +++
>  server/zlib-encoder.h                     |   47 +
>  server/zlib_encoder.c                     |  125 ---
>  server/zlib_encoder.h                     |   47 -
>  72 files changed, 9398 insertions(+), 9398 deletions(-)
>  create mode 100644 server/char-device.c
>  create mode 100644 server/char-device.h
>  delete mode 100644 server/char_device.c
>  delete mode 100644 server/char_device.h
>  create mode 100644 server/glz-encoder-dict.c
>  create mode 100644 server/glz-encoder-dict.h
>  create mode 100644 server/glz-encoder-priv.h
>  create mode 100644 server/glz-encoder.c
>  create mode 100644 server/glz-encoder.h
>  delete mode 100644 server/glz_encoder.c
>  delete mode 100644 server/glz_encoder.h
>  delete mode 100644 server/glz_encoder_dictionary.c
>  delete mode 100644 server/glz_encoder_dictionary.h
>  delete mode 100644 server/glz_encoder_dictionary_protected.h
>  create mode 100644 server/image-cache.c
>  create mode 100644 server/image-cache.h
>  create mode 100644 server/inputs-channel.c
>  create mode 100644 server/inputs-channel.h
>  delete mode 100644 server/inputs_channel.c
>  delete mode 100644 server/inputs_channel.h
>  create mode 100644 server/jpeg-encoder.c
>  create mode 100644 server/jpeg-encoder.h
>  delete mode 100644 server/jpeg_encoder.c
>  delete mode 100644 server/jpeg_encoder.h
>  create mode 100644 server/main-channel.c
>  create mode 100644 server/main-channel.h
>  create mode 100644 server/main-dispatcher.c
>  create mode 100644 server/main-dispatcher.h
>  delete mode 100644 server/main_channel.c
>  delete mode 100644 server/main_channel.h
>  delete mode 100644 server/main_dispatcher.c
>  delete mode 100644 server/main_dispatcher.h
>  create mode 100644 server/memslot.c
>  create mode 100644 server/memslot.h
>  create mode 100644 server/migration-protocol.h
>  delete mode 100644 server/migration_protocol.h
>  create mode 100644 server/mjpeg-encoder.c
>  create mode 100644 server/mjpeg-encoder.h
>  delete mode 100644 server/mjpeg_encoder.c
>  delete mode 100644 server/mjpeg_encoder.h
>  delete mode 100644 server/red_memslots.c
>  delete mode 100644 server/red_memslots.h
>  delete mode 100644 server/reds_sw_canvas.c
>  delete mode 100644 server/reds_sw_canvas.h
>  delete mode 100644 server/snd_worker.c
>  delete mode 100644 server/snd_worker.h
>  create mode 100644 server/sound.c
>  create mode 100644 server/sound.h
>  delete mode 100644 server/spice_image_cache.c
>  delete mode 100644 server/spice_image_cache.h
>  create mode 100644 server/sw-canvas.c
>  create mode 100644 server/sw-canvas.h
>  create mode 100644 server/zlib-encoder.c
>  create mode 100644 server/zlib-encoder.h
>  delete mode 100644 server/zlib_encoder.c
>  delete mode 100644 server/zlib_encoder.h
>
> diff --git a/server/Makefile.am b/server/Makefile.am
> index 6b45a42..e964fb1 100644
> --- a/server/Makefile.am
> +++ b/server/Makefile.am
> @@ -66,25 +66,25 @@ libspice_server_la_SOURCES =                        \
>         agent-msg-filter.c                      \
>         agent-msg-filter.h                      \
>         cache-item.h                            \
> -       char_device.c                           \
> -       char_device.h                           \
> +       char-device.c                           \
> +       char-device.h                           \
>         demarshallers.h                         \
> -       glz_encoder.c                           \
> -       glz_encoder.h                           \
> +       glz-encoder.c                           \
> +       glz-encoder.h                           \
>         glz_encoder_config.h                    \
> -       glz_encoder_dictionary.c                \
> -       glz_encoder_dictionary.h                \
> -       glz_encoder_dictionary_protected.h      \
> -       inputs_channel.c                        \
> -       inputs_channel.h                        \
> -       jpeg_encoder.c                          \
> -       jpeg_encoder.h                          \
> +       glz-encoder-dict.c              \
> +       glz-encoder-dict.h              \
> +       glz-encoder-priv.h      \
> +       inputs-channel.c                        \
> +       inputs-channel.h                        \
> +       jpeg-encoder.c                          \
> +       jpeg-encoder.h                          \
>         lz4_encoder.c                           \
>         lz4_encoder.h                           \
> -       main_channel.c                          \
> -       main_channel.h                          \
> -       mjpeg_encoder.c                         \
> -       mjpeg_encoder.h                         \
> +       main-channel.c                          \
> +       main-channel.h                          \
> +       mjpeg-encoder.c                         \
> +       mjpeg-encoder.h                         \
>         red_channel.c                           \
>         red_channel.h                           \
>         red_common.h                            \
> @@ -92,11 +92,11 @@ libspice_server_la_SOURCES =                        \
>         dispatcher.h                            \
>         red_dispatcher.c                        \
>         red_dispatcher.h                        \
> -       main_dispatcher.c                       \
> -       main_dispatcher.h                       \
> -       migration_protocol.h            \
> -       red_memslots.c                          \
> -       red_memslots.h                          \
> +       main-dispatcher.c                       \
> +       main-dispatcher.h                       \
> +       migration-protocol.h            \
> +       memslot.c                               \
> +       memslot.h                               \
>         red_parse_qxl.c                         \
>         red_record_qxl.c                        \
>         red_record_qxl.h                        \
> @@ -114,20 +114,20 @@ libspice_server_la_SOURCES =                      \
>         reds-private.h                          \
>         reds_stream.c                           \
>         reds_stream.h                           \
> -       reds_sw_canvas.c                        \
> -       reds_sw_canvas.h                        \
> -       snd_worker.c                            \
> -       snd_worker.h                            \
> +       sw-canvas.c                     \
> +       sw-canvas.h                     \
> +       sound.c                         \
> +       sound.h                         \
>         stat.h                                  \
>         spicevmc.c                              \
>         spice_timer_queue.c                     \
>         spice_timer_queue.h                     \
> -       zlib_encoder.c                          \
> -       zlib_encoder.h                          \
> +       zlib-encoder.c                          \
> +       zlib-encoder.h                          \
>         spice_bitmap_utils.h            \
>         spice_bitmap_utils.c            \
> -       spice_image_cache.h                     \
> -       spice_image_cache.c                     \
> +       image-cache.h                   \
> +       image-cache.c                   \
>         pixmap-cache.h                          \
>         pixmap-cache.c                          \
>         tree.h                          \
> diff --git a/server/char-device.c b/server/char-device.c
> new file mode 100644
> index 0000000..3790fab
> --- /dev/null
> +++ b/server/char-device.c
> @@ -0,0 +1,1035 @@
> +/* spice-server char device flow control code
> +
> +   Copyright (C) 2012 Red Hat, Inc.
> +
> +   Red Hat Authors:
> +   Yonit Halperin <yhalperi at redhat.com>
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http:www.gnu.org/licenses/>.
> +*/
> +
> +
> +#include <config.h>
> +#include "char-device.h"
> +#include "red_channel.h"
> +#include "reds.h"
> +
> +#define CHAR_DEVICE_WRITE_TO_TIMEOUT 100
> +#define SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT 30000
> +#define MAX_POOL_SIZE (10 * 64 * 1024)
> +
> +typedef struct SpiceCharDeviceClientState SpiceCharDeviceClientState;
> +struct SpiceCharDeviceClientState {
> +    RingItem link;
> +    SpiceCharDeviceState *dev;
> +    RedClient *client;
> +    int do_flow_control;
> +    uint64_t num_client_tokens;
> +    uint64_t num_client_tokens_free; /* client messages that were consumed by the device */
> +    uint64_t num_send_tokens; /* send to client */
> +    SpiceTimer *wait_for_tokens_timer;
> +    int wait_for_tokens_started;
> +    Ring send_queue;
> +    uint32_t send_queue_size;
> +    uint32_t max_send_queue_size;
> +};
> +
> +struct SpiceCharDeviceState {
> +    int running;
> +    int active; /* has read/write been performed since the device was started */
> +    int wait_for_migrate_data;
> +    uint32_t refs;
> +
> +    Ring write_queue;
> +    Ring write_bufs_pool;
> +    uint64_t cur_pool_size;
> +    SpiceCharDeviceWriteBuffer *cur_write_buf;
> +    uint8_t *cur_write_buf_pos;
> +    SpiceTimer *write_to_dev_timer;
> +    uint64_t num_self_tokens;
> +
> +    Ring clients; /* list of SpiceCharDeviceClientState */
> +    uint32_t num_clients;
> +
> +    uint64_t client_tokens_interval; /* frequency of returning tokens to the client */
> +    SpiceCharDeviceInstance *sin;
> +
> +    int during_read_from_device;
> +    int during_write_to_device;
> +
> +    SpiceCharDeviceCallbacks cbs;
> +    void *opaque;
> +};
> +
> +enum {
> +    WRITE_BUFFER_ORIGIN_NONE,
> +    WRITE_BUFFER_ORIGIN_CLIENT,
> +    WRITE_BUFFER_ORIGIN_SERVER,
> +    WRITE_BUFFER_ORIGIN_SERVER_NO_TOKEN,
> +};
> +
> +/* Holding references for avoiding access violation if the char device was
> + * destroyed during a callback */
> +static void spice_char_device_state_ref(SpiceCharDeviceState *char_dev);
> +static void spice_char_device_state_unref(SpiceCharDeviceState *char_dev);
> +static void spice_char_device_write_buffer_unref(SpiceCharDeviceWriteBuffer *write_buf);
> +
> +static void spice_char_dev_write_retry(void *opaque);
> +
> +typedef struct SpiceCharDeviceMsgToClientItem {
> +    RingItem link;
> +    SpiceCharDeviceMsgToClient *msg;
> +} SpiceCharDeviceMsgToClientItem;
> +
> +static void spice_char_device_write_buffer_free(SpiceCharDeviceWriteBuffer *buf)
> +{
> +    if (buf == NULL)
> +        return;
> +
> +    free(buf->buf);
> +    free(buf);
> +}
> +
> +static void write_buffers_queue_free(Ring *write_queue)
> +{
> +    while (!ring_is_empty(write_queue)) {
> +        RingItem *item = ring_get_tail(write_queue);
> +        SpiceCharDeviceWriteBuffer *buf;
> +
> +        ring_remove(item);
> +        buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
> +        spice_char_device_write_buffer_free(buf);
> +    }
> +}
> +
> +static void spice_char_device_write_buffer_pool_add(SpiceCharDeviceState *dev,
> +                                                    SpiceCharDeviceWriteBuffer *buf)
> +{
> +    if (buf->refs == 1 &&
> +        dev->cur_pool_size < MAX_POOL_SIZE) {
> +        buf->buf_used = 0;
> +        buf->origin = WRITE_BUFFER_ORIGIN_NONE;
> +        buf->client = NULL;
> +        dev->cur_pool_size += buf->buf_size;
> +        ring_add(&dev->write_bufs_pool, &buf->link);
> +        return;
> +    }
> +
> +    /* Buffer still being used - just unref for the caller */
> +    spice_char_device_write_buffer_unref(buf);
> +}
> +
> +static void spice_char_device_client_send_queue_free(SpiceCharDeviceState *dev,
> +                                                     SpiceCharDeviceClientState *dev_client)
> +{
> +    spice_debug("send_queue_empty %d", ring_is_empty(&dev_client->send_queue));
> +    while (!ring_is_empty(&dev_client->send_queue)) {
> +        RingItem *item = ring_get_tail(&dev_client->send_queue);
> +        SpiceCharDeviceMsgToClientItem *msg_item = SPICE_CONTAINEROF(item,
> +                                                                     SpiceCharDeviceMsgToClientItem,
> +                                                                     link);
> +
> +        ring_remove(item);
> +        dev->cbs.unref_msg_to_client(msg_item->msg, dev->opaque);
> +        free(msg_item);
> +    }
> +    dev_client->num_send_tokens += dev_client->send_queue_size;
> +    dev_client->send_queue_size = 0;
> +}
> +
> +static void spice_char_device_client_free(SpiceCharDeviceState *dev,
> +                                          SpiceCharDeviceClientState *dev_client)
> +{
> +    RingItem *item, *next;
> +
> +    if (dev_client->wait_for_tokens_timer) {
> +        core->timer_remove(dev_client->wait_for_tokens_timer);
> +    }
> +
> +    spice_char_device_client_send_queue_free(dev, dev_client);
> +
> +    /* remove write buffers that are associated with the client */
> +    spice_debug("write_queue_is_empty %d", ring_is_empty(&dev->write_queue) && !dev->cur_write_buf);
> +    RING_FOREACH_SAFE(item, next, &dev->write_queue) {
> +        SpiceCharDeviceWriteBuffer *write_buf;
> +
> +        write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
> +        if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
> +            write_buf->client == dev_client->client) {
> +            ring_remove(item);
> +            spice_char_device_write_buffer_pool_add(dev, write_buf);
> +        }
> +    }
> +
> +    if (dev->cur_write_buf && dev->cur_write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
> +        dev->cur_write_buf->client == dev_client->client) {
> +        dev->cur_write_buf->origin = WRITE_BUFFER_ORIGIN_NONE;
> +        dev->cur_write_buf->client = NULL;
> +    }
> +
> +    dev->num_clients--;
> +    ring_remove(&dev_client->link);
> +    free(dev_client);
> +}
> +
> +static void spice_char_device_handle_client_overflow(SpiceCharDeviceClientState *dev_client)
> +{
> +    SpiceCharDeviceState *dev = dev_client->dev;
> +    spice_printerr("dev %p client %p ", dev, dev_client);
> +    dev->cbs.remove_client(dev_client->client, dev->opaque);
> +}
> +
> +static SpiceCharDeviceClientState *spice_char_device_client_find(SpiceCharDeviceState *dev,
> +                                                                 RedClient *client)
> +{
> +    RingItem *item;
> +
> +    RING_FOREACH(item, &dev->clients) {
> +        SpiceCharDeviceClientState *dev_client;
> +
> +        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
> +        if (dev_client->client == client) {
> +            return dev_client;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +/***************************
> + * Reading from the device *
> + **************************/
> +
> +static void device_client_wait_for_tokens_timeout(void *opaque)
> +{
> +    SpiceCharDeviceClientState *dev_client = opaque;
> +
> +    spice_char_device_handle_client_overflow(dev_client);
> +}
> +
> +static int spice_char_device_can_send_to_client(SpiceCharDeviceClientState *dev_client)
> +{
> +    return !dev_client->do_flow_control || dev_client->num_send_tokens;
> +}
> +
> +static uint64_t spice_char_device_max_send_tokens(SpiceCharDeviceState *dev)
> +{
> +    RingItem *item;
> +    uint64_t max = 0;
> +
> +    RING_FOREACH(item, &dev->clients) {
> +        SpiceCharDeviceClientState *dev_client;
> +
> +        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
> +
> +        if (!dev_client->do_flow_control) {
> +            max = ~0;
> +            break;
> +        }
> +
> +        if (dev_client->num_send_tokens > max) {
> +            max = dev_client->num_send_tokens;
> +        }
> +    }
> +    return max;
> +}
> +
> +static void spice_char_device_add_msg_to_client_queue(SpiceCharDeviceClientState *dev_client,
> +                                                      SpiceCharDeviceMsgToClient *msg)
> +{
> +    SpiceCharDeviceState *dev = dev_client->dev;
> +    SpiceCharDeviceMsgToClientItem *msg_item;
> +
> +    if (dev_client->send_queue_size >= dev_client->max_send_queue_size) {
> +        spice_char_device_handle_client_overflow(dev_client);
> +        return;
> +    }
> +
> +    msg_item = spice_new0(SpiceCharDeviceMsgToClientItem, 1);
> +    msg_item->msg = dev->cbs.ref_msg_to_client(msg, dev->opaque);
> +    ring_add(&dev_client->send_queue, &msg_item->link);
> +    dev_client->send_queue_size++;
> +    if (!dev_client->wait_for_tokens_started) {
> +        core->timer_start(dev_client->wait_for_tokens_timer,
> +                          SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT);
> +        dev_client->wait_for_tokens_started = TRUE;
> +    }
> +}
> +
> +static void spice_char_device_send_msg_to_clients(SpiceCharDeviceState *dev,
> +                                                  SpiceCharDeviceMsgToClient *msg)
> +{
> +    RingItem *item, *next;
> +
> +    RING_FOREACH_SAFE(item, next, &dev->clients) {
> +        SpiceCharDeviceClientState *dev_client;
> +
> +        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
> +        if (spice_char_device_can_send_to_client(dev_client)) {
> +            dev_client->num_send_tokens--;
> +            spice_assert(ring_is_empty(&dev_client->send_queue));
> +            dev->cbs.send_msg_to_client(msg, dev_client->client, dev->opaque);
> +
> +            /* don't refer to dev_client anymore, it may have been released */
> +        } else {
> +            spice_char_device_add_msg_to_client_queue(dev_client, msg);
> +        }
> +    }
> +}
> +
> +static int spice_char_device_read_from_device(SpiceCharDeviceState *dev)
> +{
> +    uint64_t max_send_tokens;
> +    int did_read = FALSE;
> +
> +    if (!dev->running || dev->wait_for_migrate_data || !dev->sin) {
> +        return FALSE;
> +    }
> +
> +    /* There are 2 scenarios where we can get called recursively:
> +     * 1) spice-vmc vmc_read triggering flush of throttled data, recalling wakeup
> +     * (virtio)
> +     * 2) in case of sending messages to the client, and unreferencing the
> +     * msg, we trigger another read.
> +     */
> +    if (dev->during_read_from_device++ > 0) {
> +        return FALSE;
> +    }
> +
> +    max_send_tokens = spice_char_device_max_send_tokens(dev);
> +    spice_char_device_state_ref(dev);
> +    /*
> +     * Reading from the device only in case at least one of the clients have a free token.
> +     * All messages will be discarded if no client is attached to the device
> +     */
> +    while ((max_send_tokens || ring_is_empty(&dev->clients)) && dev->running) {
> +        SpiceCharDeviceMsgToClient *msg;
> +
> +        msg = dev->cbs.read_one_msg_from_device(dev->sin, dev->opaque);
> +        if (!msg) {
> +            if (dev->during_read_from_device > 1) {
> +                dev->during_read_from_device = 1;
> +                continue; /* a wakeup might have been called during the read -
> +                             make sure it doesn't get lost */
> +            }
> +            break;
> +        }
> +        did_read = TRUE;
> +        spice_char_device_send_msg_to_clients(dev, msg);
> +        dev->cbs.unref_msg_to_client(msg, dev->opaque);
> +        max_send_tokens--;
> +    }
> +    dev->during_read_from_device = 0;
> +    if (dev->running) {
> +        dev->active = dev->active || did_read;
> +    }
> +    spice_char_device_state_unref(dev);
> +    return did_read;
> +}
> +
> +static void spice_char_device_client_send_queue_push(SpiceCharDeviceClientState *dev_client)
> +{
> +    RingItem *item;
> +    while ((item = ring_get_tail(&dev_client->send_queue)) &&
> +           spice_char_device_can_send_to_client(dev_client)) {
> +        SpiceCharDeviceMsgToClientItem *msg_item;
> +
> +        msg_item = SPICE_CONTAINEROF(item, SpiceCharDeviceMsgToClientItem, link);
> +        ring_remove(item);
> +
> +        dev_client->num_send_tokens--;
> +        dev_client->dev->cbs.send_msg_to_client(msg_item->msg,
> +                                           dev_client->client,
> +                                           dev_client->dev->opaque);
> +        dev_client->dev->cbs.unref_msg_to_client(msg_item->msg, dev_client->dev->opaque);
> +        dev_client->send_queue_size--;
> +        free(msg_item);
> +    }
> +}
> +
> +static void spice_char_device_send_to_client_tokens_absorb(SpiceCharDeviceClientState *dev_client,
> +                                                           uint32_t tokens)
> +{
> +    dev_client->num_send_tokens += tokens;
> +
> +    if (dev_client->send_queue_size) {
> +        spice_assert(dev_client->num_send_tokens == tokens);
> +        spice_char_device_client_send_queue_push(dev_client);
> +    }
> +
> +    if (spice_char_device_can_send_to_client(dev_client)) {
> +        core->timer_cancel(dev_client->wait_for_tokens_timer);
> +        dev_client->wait_for_tokens_started = FALSE;
> +        spice_char_device_read_from_device(dev_client->dev);
> +    } else if (dev_client->send_queue_size) {
> +        core->timer_start(dev_client->wait_for_tokens_timer,
> +                          SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT);
> +        dev_client->wait_for_tokens_started = TRUE;
> +    }
> +}
> +
> +void spice_char_device_send_to_client_tokens_add(SpiceCharDeviceState *dev,
> +                                                 RedClient *client,
> +                                                 uint32_t tokens)
> +{
> +    SpiceCharDeviceClientState *dev_client;
> +
> +    dev_client = spice_char_device_client_find(dev, client);
> +
> +    if (!dev_client) {
> +        spice_error("client wasn't found dev %p client %p", dev, client);
> +        return;
> +    }
> +    spice_char_device_send_to_client_tokens_absorb(dev_client, tokens);
> +}
> +
> +void spice_char_device_send_to_client_tokens_set(SpiceCharDeviceState *dev,
> +                                                 RedClient *client,
> +                                                 uint32_t tokens)
> +{
> +    SpiceCharDeviceClientState *dev_client;
> +
> +    dev_client = spice_char_device_client_find(dev, client);
> +
> +    if (!dev_client) {
> +        spice_error("client wasn't found dev %p client %p", dev, client);
> +        return;
> +    }
> +
> +    dev_client->num_send_tokens = 0;
> +    spice_char_device_send_to_client_tokens_absorb(dev_client, tokens);
> +}
> +
> +/**************************
> + * Writing to the device  *
> +***************************/
> +
> +static void spice_char_device_client_tokens_add(SpiceCharDeviceState *dev,
> +                                                SpiceCharDeviceClientState *dev_client,
> +                                                uint32_t num_tokens)
> +{
> +    if (!dev_client->do_flow_control) {
> +        return;
> +    }
> +    if (num_tokens > 1) {
> +        spice_debug("#tokens > 1 (=%u)", num_tokens);
> +    }
> +    dev_client->num_client_tokens_free += num_tokens;
> +    if (dev_client->num_client_tokens_free >= dev->client_tokens_interval) {
> +        uint32_t tokens = dev_client->num_client_tokens_free;
> +
> +        dev_client->num_client_tokens += dev_client->num_client_tokens_free;
> +        dev_client->num_client_tokens_free = 0;
> +        dev->cbs.send_tokens_to_client(dev_client->client,
> +                                       tokens,
> +                                       dev->opaque);
> +    }
> +}
> +
> +static int spice_char_device_write_to_device(SpiceCharDeviceState *dev)
> +{
> +    SpiceCharDeviceInterface *sif;
> +    int total = 0;
> +    int n;
> +
> +    if (!dev->running || dev->wait_for_migrate_data || !dev->sin) {
> +        return 0;
> +    }
> +
> +    /* protect against recursion with spice_char_device_wakeup */
> +    if (dev->during_write_to_device++ > 0) {
> +        return 0;
> +    }
> +
> +    spice_char_device_state_ref(dev);
> +
> +    if (dev->write_to_dev_timer) {
> +        core->timer_cancel(dev->write_to_dev_timer);
> +    }
> +
> +    sif = SPICE_CONTAINEROF(dev->sin->base.sif, SpiceCharDeviceInterface, base);
> +    while (dev->running) {
> +        uint32_t write_len;
> +
> +        if (!dev->cur_write_buf) {
> +            RingItem *item = ring_get_tail(&dev->write_queue);
> +            if (!item) {
> +                break;
> +            }
> +            dev->cur_write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
> +            dev->cur_write_buf_pos = dev->cur_write_buf->buf;
> +            ring_remove(item);
> +        }
> +
> +        write_len = dev->cur_write_buf->buf + dev->cur_write_buf->buf_used -
> +                    dev->cur_write_buf_pos;
> +        n = sif->write(dev->sin, dev->cur_write_buf_pos, write_len);
> +        if (n <= 0) {
> +            if (dev->during_write_to_device > 1) {
> +                dev->during_write_to_device = 1;
> +                continue; /* a wakeup might have been called during the write -
> +                             make sure it doesn't get lost */
> +            }
> +            break;
> +        }
> +        total += n;
> +        write_len -= n;
> +        if (!write_len) {
> +            SpiceCharDeviceWriteBuffer *release_buf = dev->cur_write_buf;
> +            dev->cur_write_buf = NULL;
> +            spice_char_device_write_buffer_release(dev, release_buf);
> +            continue;
> +        }
> +        dev->cur_write_buf_pos += n;
> +    }
> +    /* retry writing as long as the write queue is not empty */
> +    if (dev->running) {
> +        if (dev->cur_write_buf) {
> +            if (dev->write_to_dev_timer) {
> +                core->timer_start(dev->write_to_dev_timer,
> +                                  CHAR_DEVICE_WRITE_TO_TIMEOUT);
> +            }
> +        } else {
> +            spice_assert(ring_is_empty(&dev->write_queue));
> +        }
> +        dev->active = dev->active || total;
> +    }
> +    dev->during_write_to_device = 0;
> +    spice_char_device_state_unref(dev);
> +    return total;
> +}
> +
> +static void spice_char_dev_write_retry(void *opaque)
> +{
> +    SpiceCharDeviceState *dev = opaque;
> +
> +    if (dev->write_to_dev_timer) {
> +        core->timer_cancel(dev->write_to_dev_timer);
> +    }
> +    spice_char_device_write_to_device(dev);
> +}
> +
> +static SpiceCharDeviceWriteBuffer *__spice_char_device_write_buffer_get(
> +    SpiceCharDeviceState *dev, RedClient *client,
> +    int size, int origin, int migrated_data_tokens)
> +{
> +    RingItem *item;
> +    SpiceCharDeviceWriteBuffer *ret;
> +
> +    if (origin == WRITE_BUFFER_ORIGIN_SERVER && !dev->num_self_tokens) {
> +        return NULL;
> +    }
> +
> +    if ((item = ring_get_tail(&dev->write_bufs_pool))) {
> +        ret = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
> +        ring_remove(item);
> +        dev->cur_pool_size -= ret->buf_size;
> +    } else {
> +        ret = spice_new0(SpiceCharDeviceWriteBuffer, 1);
> +    }
> +
> +    spice_assert(!ret->buf_used);
> +
> +    if (ret->buf_size < size) {
> +        ret->buf = spice_realloc(ret->buf, size);
> +        ret->buf_size = size;
> +    }
> +    ret->origin = origin;
> +
> +    if (origin == WRITE_BUFFER_ORIGIN_CLIENT) {
> +       spice_assert(client);
> +       SpiceCharDeviceClientState *dev_client = spice_char_device_client_find(dev, client);
> +       if (dev_client) {
> +            if (!migrated_data_tokens &&
> +                dev_client->do_flow_control && !dev_client->num_client_tokens) {
> +                spice_printerr("token violation: dev %p client %p", dev, client);
> +                spice_char_device_handle_client_overflow(dev_client);
> +                goto error;
> +            }
> +            ret->client = client;
> +            if (!migrated_data_tokens && dev_client->do_flow_control) {
> +                dev_client->num_client_tokens--;
> +            }
> +        } else {
> +            /* it is possible that the client was removed due to send tokens underflow, but
> +             * the caller still receive messages from the client */
> +            spice_printerr("client not found: dev %p client %p", dev, client);
> +            goto error;
> +        }
> +    } else if (origin == WRITE_BUFFER_ORIGIN_SERVER) {
> +        dev->num_self_tokens--;
> +    }
> +
> +    ret->token_price = migrated_data_tokens ? migrated_data_tokens : 1;
> +    ret->refs = 1;
> +    return ret;
> +error:
> +    dev->cur_pool_size += ret->buf_size;
> +    ring_add(&dev->write_bufs_pool, &ret->link);
> +    return NULL;
> +}
> +
> +SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get(SpiceCharDeviceState *dev,
> +                                                               RedClient *client,
> +                                                               int size)
> +{
> +   return  __spice_char_device_write_buffer_get(dev, client, size,
> +             client ? WRITE_BUFFER_ORIGIN_CLIENT : WRITE_BUFFER_ORIGIN_SERVER,
> +             0);
> +}
> +
> +SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get_server_no_token(
> +    SpiceCharDeviceState *dev, int size)
> +{
> +   return  __spice_char_device_write_buffer_get(dev, NULL, size,
> +             WRITE_BUFFER_ORIGIN_SERVER_NO_TOKEN, 0);
> +}
> +
> +static SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_ref(SpiceCharDeviceWriteBuffer *write_buf)
> +{
> +    spice_assert(write_buf);
> +
> +    write_buf->refs++;
> +    return write_buf;
> +}
> +
> +static void spice_char_device_write_buffer_unref(SpiceCharDeviceWriteBuffer *write_buf)
> +{
> +    spice_assert(write_buf);
> +
> +    write_buf->refs--;
> +    if (write_buf->refs == 0)
> +        spice_char_device_write_buffer_free(write_buf);
> +}
> +
> +void spice_char_device_write_buffer_add(SpiceCharDeviceState *dev,
> +                                        SpiceCharDeviceWriteBuffer *write_buf)
> +{
> +    spice_assert(dev);
> +    /* caller shouldn't add buffers for client that was removed */
> +    if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
> +        !spice_char_device_client_find(dev, write_buf->client)) {
> +        spice_printerr("client not found: dev %p client %p", dev, write_buf->client);
> +        spice_char_device_write_buffer_pool_add(dev, write_buf);
> +        return;
> +    }
> +
> +    ring_add(&dev->write_queue, &write_buf->link);
> +    spice_char_device_write_to_device(dev);
> +}
> +
> +void spice_char_device_write_buffer_release(SpiceCharDeviceState *dev,
> +                                            SpiceCharDeviceWriteBuffer *write_buf)
> +{
> +    int buf_origin = write_buf->origin;
> +    uint32_t buf_token_price = write_buf->token_price;
> +    RedClient *client = write_buf->client;
> +
> +    spice_assert(!ring_item_is_linked(&write_buf->link));
> +    if (!dev) {
> +        spice_printerr("no device. write buffer is freed");
> +        spice_char_device_write_buffer_free(write_buf);
> +        return;
> +    }
> +
> +    spice_assert(dev->cur_write_buf != write_buf);
> +
> +    spice_char_device_write_buffer_pool_add(dev, write_buf);
> +    if (buf_origin == WRITE_BUFFER_ORIGIN_CLIENT) {
> +        SpiceCharDeviceClientState *dev_client;
> +
> +        spice_assert(client);
> +        dev_client = spice_char_device_client_find(dev, client);
> +        /* when a client is removed, we remove all the buffers that are associated with it */
> +        spice_assert(dev_client);
> +        spice_char_device_client_tokens_add(dev, dev_client, buf_token_price);
> +    } else if (buf_origin == WRITE_BUFFER_ORIGIN_SERVER) {
> +        dev->num_self_tokens++;
> +        if (dev->cbs.on_free_self_token) {
> +            dev->cbs.on_free_self_token(dev->opaque);
> +        }
> +    }
> +}
> +
> +/********************************
> + * char_device_state management *
> + ********************************/
> +
> +SpiceCharDeviceState *spice_char_device_state_create(SpiceCharDeviceInstance *sin,
> +                                                     uint32_t client_tokens_interval,
> +                                                     uint32_t self_tokens,
> +                                                     SpiceCharDeviceCallbacks *cbs,
> +                                                     void *opaque)
> +{
> +    SpiceCharDeviceState *char_dev;
> +    SpiceCharDeviceInterface *sif;
> +
> +    spice_assert(sin);
> +    spice_assert(cbs->read_one_msg_from_device && cbs->ref_msg_to_client &&
> +                 cbs->unref_msg_to_client && cbs->send_msg_to_client &&
> +                 cbs->send_tokens_to_client && cbs->remove_client);
> +
> +    char_dev = spice_new0(SpiceCharDeviceState, 1);
> +    char_dev->sin = sin;
> +    char_dev->cbs = *cbs;
> +    char_dev->opaque = opaque;
> +    char_dev->client_tokens_interval = client_tokens_interval;
> +    char_dev->num_self_tokens = self_tokens;
> +
> +    ring_init(&char_dev->write_queue);
> +    ring_init(&char_dev->write_bufs_pool);
> +    ring_init(&char_dev->clients);
> +
> +    sif = SPICE_CONTAINEROF(char_dev->sin->base.sif, SpiceCharDeviceInterface, base);
> +    if (sif->base.minor_version <= 2 ||
> +        !(sif->flags & SPICE_CHAR_DEVICE_NOTIFY_WRITABLE)) {
> +        char_dev->write_to_dev_timer = core->timer_add(spice_char_dev_write_retry, char_dev);
> +        if (!char_dev->write_to_dev_timer) {
> +            spice_error("failed creating char dev write timer");
> +        }
> +    }
> +
> +    char_dev->refs = 1;
> +    sin->st = char_dev;
> +    spice_debug("sin %p dev_state %p", sin, char_dev);
> +    return char_dev;
> +}
> +
> +void spice_char_device_state_reset_dev_instance(SpiceCharDeviceState *state,
> +                                                SpiceCharDeviceInstance *sin)
> +{
> +    spice_debug("sin %p dev_state %p", sin, state);
> +    state->sin = sin;
> +    sin->st = state;
> +}
> +
> +void *spice_char_device_state_opaque_get(SpiceCharDeviceState *dev)
> +{
> +    return dev->opaque;
> +}
> +
> +static void spice_char_device_state_ref(SpiceCharDeviceState *char_dev)
> +{
> +    char_dev->refs++;
> +}
> +
> +static void spice_char_device_state_unref(SpiceCharDeviceState *char_dev)
> +{
> +    /* The refs field protects the char_dev from being deallocated in
> +     * case spice_char_device_state_destroy has been called
> +     * during a callabck, and we might still access the char_dev afterwards.
> +     * spice_char_device_state_unref is always coupled with a preceding
> +     * spice_char_device_state_ref. Here, refs can turn 0
> +     * only when spice_char_device_state_destroy is called in between
> +     * the calls to spice_char_device_state_ref and spice_char_device_state_unref.*/
> +    if (!--char_dev->refs) {
> +        free(char_dev);
> +    }
> +}
> +
> +void spice_char_device_state_destroy(SpiceCharDeviceState *char_dev)
> +{
> +    reds_on_char_device_state_destroy(char_dev);
> +    if (char_dev->write_to_dev_timer) {
> +        core->timer_remove(char_dev->write_to_dev_timer);
> +        char_dev->write_to_dev_timer = NULL;
> +    }
> +    write_buffers_queue_free(&char_dev->write_queue);
> +    write_buffers_queue_free(&char_dev->write_bufs_pool);
> +    char_dev->cur_pool_size = 0;
> +    spice_char_device_write_buffer_free(char_dev->cur_write_buf);
> +    char_dev->cur_write_buf = NULL;
> +
> +    while (!ring_is_empty(&char_dev->clients)) {
> +        RingItem *item = ring_get_tail(&char_dev->clients);
> +        SpiceCharDeviceClientState *dev_client;
> +
> +        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
> +        spice_char_device_client_free(char_dev, dev_client);
> +    }
> +    char_dev->running = FALSE;
> +
> +    spice_char_device_state_unref(char_dev);
> +}
> +
> +int spice_char_device_client_add(SpiceCharDeviceState *dev,
> +                                 RedClient *client,
> +                                 int do_flow_control,
> +                                 uint32_t max_send_queue_size,
> +                                 uint32_t num_client_tokens,
> +                                 uint32_t num_send_tokens,
> +                                 int wait_for_migrate_data)
> +{
> +    SpiceCharDeviceClientState *dev_client;
> +
> +    spice_assert(dev);
> +    spice_assert(client);
> +
> +    if (wait_for_migrate_data && (dev->num_clients > 0 || dev->active)) {
> +        spice_warning("can't restore device %p from migration data. The device "
> +                      "has already been active", dev);
> +        return FALSE;
> +    }
> +
> +    dev->wait_for_migrate_data = wait_for_migrate_data;
> +
> +    spice_debug("dev_state %p client %p", dev, client);
> +    dev_client = spice_new0(SpiceCharDeviceClientState, 1);
> +    dev_client->dev = dev;
> +    dev_client->client = client;
> +    ring_init(&dev_client->send_queue);
> +    dev_client->send_queue_size = 0;
> +    dev_client->max_send_queue_size = max_send_queue_size;
> +    dev_client->do_flow_control = do_flow_control;
> +    if (do_flow_control) {
> +        dev_client->wait_for_tokens_timer = core->timer_add(device_client_wait_for_tokens_timeout,
> +                                                            dev_client);
> +        if (!dev_client->wait_for_tokens_timer) {
> +            spice_error("failed to create wait for tokens timer");
> +        }
> +        dev_client->num_client_tokens = num_client_tokens;
> +        dev_client->num_send_tokens = num_send_tokens;
> +    } else {
> +        dev_client->num_client_tokens = ~0;
> +        dev_client->num_send_tokens = ~0;
> +    }
> +    ring_add(&dev->clients, &dev_client->link);
> +    dev->num_clients++;
> +    /* Now that we have a client, forward any pending device data */
> +    spice_char_device_wakeup(dev);
> +    return TRUE;
> +}
> +
> +void spice_char_device_client_remove(SpiceCharDeviceState *dev,
> +                                     RedClient *client)
> +{
> +    SpiceCharDeviceClientState *dev_client;
> +
> +    spice_debug("dev_state %p client %p", dev, client);
> +    dev_client = spice_char_device_client_find(dev, client);
> +
> +    if (!dev_client) {
> +        spice_error("client wasn't found");
> +        return;
> +    }
> +    spice_char_device_client_free(dev, dev_client);
> +    if (dev->wait_for_migrate_data) {
> +        spice_assert(dev->num_clients == 0);
> +        dev->wait_for_migrate_data  = FALSE;
> +        spice_char_device_read_from_device(dev);
> +    }
> +
> +    if (dev->num_clients == 0) {
> +        spice_debug("client removed, memory pool will be freed (%lu bytes)", dev->cur_pool_size);
> +        write_buffers_queue_free(&dev->write_bufs_pool);
> +        dev->cur_pool_size = 0;
> +    }
> +}
> +
> +int spice_char_device_client_exists(SpiceCharDeviceState *dev,
> +                                    RedClient *client)
> +{
> +    return (spice_char_device_client_find(dev, client) != NULL);
> +}
> +
> +void spice_char_device_start(SpiceCharDeviceState *dev)
> +{
> +    spice_debug("dev_state %p", dev);
> +    dev->running = TRUE;
> +    spice_char_device_state_ref(dev);
> +    while (spice_char_device_write_to_device(dev) ||
> +           spice_char_device_read_from_device(dev));
> +    spice_char_device_state_unref(dev);
> +}
> +
> +void spice_char_device_stop(SpiceCharDeviceState *dev)
> +{
> +    spice_debug("dev_state %p", dev);
> +    dev->running = FALSE;
> +    dev->active = FALSE;
> +    if (dev->write_to_dev_timer) {
> +        core->timer_cancel(dev->write_to_dev_timer);
> +    }
> +}
> +
> +void spice_char_device_reset(SpiceCharDeviceState *dev)
> +{
> +    RingItem *client_item;
> +
> +    spice_char_device_stop(dev);
> +    dev->wait_for_migrate_data = FALSE;
> +    spice_debug("dev_state %p", dev);
> +    while (!ring_is_empty(&dev->write_queue)) {
> +        RingItem *item = ring_get_tail(&dev->write_queue);
> +        SpiceCharDeviceWriteBuffer *buf;
> +
> +        ring_remove(item);
> +        buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
> +        /* tracking the tokens */
> +        spice_char_device_write_buffer_release(dev, buf);
> +    }
> +    if (dev->cur_write_buf) {
> +        SpiceCharDeviceWriteBuffer *release_buf = dev->cur_write_buf;
> +
> +        dev->cur_write_buf = NULL;
> +        spice_char_device_write_buffer_release(dev, release_buf);
> +    }
> +
> +    RING_FOREACH(client_item, &dev->clients) {
> +        SpiceCharDeviceClientState *dev_client;
> +
> +        dev_client = SPICE_CONTAINEROF(client_item, SpiceCharDeviceClientState, link);
> +        spice_char_device_client_send_queue_free(dev, dev_client);
> +    }
> +    dev->sin = NULL;
> +}
> +
> +void spice_char_device_wakeup(SpiceCharDeviceState *dev)
> +{
> +    spice_char_device_write_to_device(dev);
> +    spice_char_device_read_from_device(dev);
> +}
> +
> +/*************
> + * Migration *
> + * **********/
> +
> +void spice_char_device_state_migrate_data_marshall_empty(SpiceMarshaller *m)
> +{
> +    SpiceMigrateDataCharDevice *mig_data;
> +
> +    spice_debug(NULL);
> +    mig_data = (SpiceMigrateDataCharDevice *)spice_marshaller_reserve_space(m,
> +                                                                            sizeof(*mig_data));
> +    memset(mig_data, 0, sizeof(*mig_data));
> +    mig_data->version = SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION;
> +    mig_data->connected = FALSE;
> +}
> +
> +static void migrate_data_marshaller_write_buffer_free(uint8_t *data, void *opaque)
> +{
> +    SpiceCharDeviceWriteBuffer *write_buf = (SpiceCharDeviceWriteBuffer *)opaque;
> +
> +    spice_char_device_write_buffer_unref(write_buf);
> +}
> +
> +void spice_char_device_state_migrate_data_marshall(SpiceCharDeviceState *dev,
> +                                                   SpiceMarshaller *m)
> +{
> +    SpiceCharDeviceClientState *client_state;
> +    RingItem *item;
> +    uint32_t *write_to_dev_size_ptr;
> +    uint32_t *write_to_dev_tokens_ptr;
> +    SpiceMarshaller *m2;
> +
> +    /* multi-clients are not supported */
> +    spice_assert(dev->num_clients == 1);
> +    client_state = SPICE_CONTAINEROF(ring_get_tail(&dev->clients),
> +                                     SpiceCharDeviceClientState,
> +                                     link);
> +    /* FIXME: if there were more than one client before the marshalling,
> +     * it is possible that the send_queue_size > 0, and the send data
> +     * should be migrated as well */
> +    spice_assert(client_state->send_queue_size == 0);
> +    spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION);
> +    spice_marshaller_add_uint8(m, 1); /* connected */
> +    spice_marshaller_add_uint32(m, client_state->num_client_tokens);
> +    spice_marshaller_add_uint32(m, client_state->num_send_tokens);
> +    write_to_dev_size_ptr = (uint32_t *)spice_marshaller_reserve_space(m, sizeof(uint32_t));
> +    write_to_dev_tokens_ptr = (uint32_t *)spice_marshaller_reserve_space(m, sizeof(uint32_t));
> +    *write_to_dev_size_ptr = 0;
> +    *write_to_dev_tokens_ptr = 0;
> +
> +    m2 = spice_marshaller_get_ptr_submarshaller(m, 0);
> +    if (dev->cur_write_buf) {
> +        uint32_t buf_remaining = dev->cur_write_buf->buf + dev->cur_write_buf->buf_used -
> +                                 dev->cur_write_buf_pos;
> +        spice_marshaller_add_ref_full(m2, dev->cur_write_buf_pos, buf_remaining,
> +                                      migrate_data_marshaller_write_buffer_free,
> +                                      spice_char_device_write_buffer_ref(dev->cur_write_buf)
> +                                      );
> +        *write_to_dev_size_ptr += buf_remaining;
> +        if (dev->cur_write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT) {
> +            spice_assert(dev->cur_write_buf->client == client_state->client);
> +            (*write_to_dev_tokens_ptr) += dev->cur_write_buf->token_price;
> +        }
> +    }
> +
> +    RING_FOREACH_REVERSED(item, &dev->write_queue) {
> +        SpiceCharDeviceWriteBuffer *write_buf;
> +
> +        write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
> +        spice_marshaller_add_ref_full(m2, write_buf->buf, write_buf->buf_used,
> +                                      migrate_data_marshaller_write_buffer_free,
> +                                      spice_char_device_write_buffer_ref(write_buf)
> +                                      );
> +        *write_to_dev_size_ptr += write_buf->buf_used;
> +        if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT) {
> +            spice_assert(write_buf->client == client_state->client);
> +            (*write_to_dev_tokens_ptr) += write_buf->token_price;
> +        }
> +    }
> +    spice_debug("migration data dev %p: write_queue size %u tokens %u",
> +                dev, *write_to_dev_size_ptr, *write_to_dev_tokens_ptr);
> +}
> +
> +int spice_char_device_state_restore(SpiceCharDeviceState *dev,
> +                                    SpiceMigrateDataCharDevice *mig_data)
> +{
> +    SpiceCharDeviceClientState *client_state;
> +    uint32_t client_tokens_window;
> +
> +    spice_assert(dev->num_clients == 1 && dev->wait_for_migrate_data);
> +
> +    client_state = SPICE_CONTAINEROF(ring_get_tail(&dev->clients),
> +                                     SpiceCharDeviceClientState,
> +                                     link);
> +    if (mig_data->version > SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION) {
> +        spice_error("dev %p error: migration data version %u is bigger than self %u",
> +                    dev, mig_data->version, SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION);
> +        return FALSE;
> +    }
> +    spice_assert(!dev->cur_write_buf && ring_is_empty(&dev->write_queue));
> +    spice_assert(mig_data->connected);
> +
> +    client_tokens_window = client_state->num_client_tokens; /* initial state of tokens */
> +    client_state->num_client_tokens = mig_data->num_client_tokens;
> +    /* assumption: client_tokens_window stays the same across severs */
> +    client_state->num_client_tokens_free = client_tokens_window -
> +                                           mig_data->num_client_tokens -
> +                                           mig_data->write_num_client_tokens;
> +    client_state->num_send_tokens = mig_data->num_send_tokens;
> +
> +    if (mig_data->write_size > 0) {
> +        if (mig_data->write_num_client_tokens) {
> +            dev->cur_write_buf =
> +                __spice_char_device_write_buffer_get(dev, client_state->client,
> +                    mig_data->write_size, WRITE_BUFFER_ORIGIN_CLIENT,
> +                    mig_data->write_num_client_tokens);
> +        } else {
> +            dev->cur_write_buf =
> +                __spice_char_device_write_buffer_get(dev, NULL,
> +                    mig_data->write_size, WRITE_BUFFER_ORIGIN_SERVER, 0);
> +        }
> +        /* the first write buffer contains all the data that was saved for migration */
> +        memcpy(dev->cur_write_buf->buf,
> +               ((uint8_t *)mig_data) + mig_data->write_data_ptr - sizeof(SpiceMigrateDataHeader),
> +               mig_data->write_size);
> +        dev->cur_write_buf->buf_used = mig_data->write_size;
> +        dev->cur_write_buf_pos = dev->cur_write_buf->buf;
> +    }
> +    dev->wait_for_migrate_data = FALSE;
> +    spice_char_device_write_to_device(dev);
> +    spice_char_device_read_from_device(dev);
> +    return TRUE;
> +}
> diff --git a/server/char-device.h b/server/char-device.h
> new file mode 100644
> index 0000000..7449c6c
> --- /dev/null
> +++ b/server/char-device.h
> @@ -0,0 +1,216 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifndef CHAR_DEVICE_H_
> +#define CHAR_DEVICE_H_
> +
> +#include "spice.h"
> +#include "red_channel.h"
> +#include "migration-protocol.h"
> +
> +/*
> + * Shared code for char devices, mainly for flow control.
> + *
> + * How to use the api:
> + * ==================
> + * device attached: call spice_char_device_state_create
> + * device detached: call spice_char_device_state_destroy/reset
> + *
> + * client connected and associated with a device: spice_char_device_client_add
> + * client disconnected: spice_char_device_client_remove
> + *
> + * Writing to the device
> + * ---------------------
> + * Write the data into SpiceCharDeviceWriteBuffer:
> + * call spice_char_device_write_buffer_get in order to get an appropriate buffer.
> + * call spice_char_device_write_buffer_add in order to push the buffer to the write queue.
> + * If you choose not to push the buffer to the device, call
> + * spice_char_device_write_buffer_release
> + *
> + * reading from the device
> + * -----------------------
> + *  The callback read_one_msg_from_device (see below) should be implemented
> + *  (using sif->read).
> + *  When the device is ready, this callback is called, and is expected to
> + *  return one message which is addressed to the client, or NULL if the read
> + *  hasn't completed.
> + *
> + * calls triggered from the device (qemu):
> + * --------------------------------------
> + * spice_char_device_start
> + * spice_char_device_stop
> + * spice_char_device_wakeup (for reading from the device)
> + */
> +
> +/*
> + * Note about multiple-clients:
> + * Multiclients are currently not supported in any of the character devices:
> + * spicevmc does not allow more than one client (and at least for usb, it should stay this way).
> + * smartcard code is not compatible with more than one reader.
> + * The server and guest agent code doesn't distinguish messages from different clients.
> + * In addition, its current flow control code (e.g., tokens handling) is wrong and doesn't
> + * take into account the different clients.
> + *
> + * Nonetheless, the following code introduces some support for multiple-clients:
> + * We track the number of tokens for all the clients, and we read from the device
> + * if one of the clients have enough tokens. For the clients that don't have tokens,
> + * we queue the messages, till they receive tokens, or till a timeout.
> + *
> + * TODO:
> + * At least for the agent, not all the messages from the device will be directed to all
> + * the clients (e.g., copy from guest to a specific client). Thus, support for
> + * client-specific-messages should be added.
> + * In addition, we should have support for clients that are being connected
> + * in the middle of a message transfer from the agent to the clients.
> + *
> + * */
> +
> +/* buffer that is used for writing to the device */
> +typedef struct SpiceCharDeviceWriteBuffer {
> +    RingItem link;
> +    int origin;
> +    RedClient *client; /* The client that sent the message to the device.
> +                          NULL if the server created the message */
> +
> +    uint8_t *buf;
> +    uint32_t buf_size;
> +    uint32_t buf_used;
> +    uint32_t token_price;
> +    uint32_t refs;
> +} SpiceCharDeviceWriteBuffer;
> +
> +typedef void SpiceCharDeviceMsgToClient;
> +
> +typedef struct SpiceCharDeviceCallbacks {
> +    /*
> +     * Messages that are addressed to the client can be queued in case we have
> +     * multiple clients and some of them don't have enough tokens.
> +     */
> +
> +    /* reads from the device till reaching a msg that should be sent to the client,
> +     * or till the reading fails */
> +    SpiceCharDeviceMsgToClient* (*read_one_msg_from_device)(SpiceCharDeviceInstance *sin,
> +                                                            void *opaque);
> +    SpiceCharDeviceMsgToClient* (*ref_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
> +                                                     void *opaque);
> +    void (*unref_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
> +                                void *opaque);
> +    void (*send_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
> +                               RedClient *client,
> +                               void *opaque); /* after this call, the message is unreferenced */
> +
> +    /* The cb is called when a predefined number of write buffers were consumed by the
> +     * device */
> +    void (*send_tokens_to_client)(RedClient *client, uint32_t tokens, void *opaque);
> +
> +    /* The cb is called when a server (self) message that was addressed to the device,
> +     * has been completely written to it */
> +    void (*on_free_self_token)(void *opaque);
> +
> +    /* This cb is called if it is recommanded that a client will be removed
> +     * due to slow flow or due to some other error.
> +     * The called instance should disconnect the client, or at least the corresponding channel */
> +    void (*remove_client)(RedClient *client, void *opaque);
> +} SpiceCharDeviceCallbacks;
> +
> +SpiceCharDeviceState *spice_char_device_state_create(SpiceCharDeviceInstance *sin,
> +                                                     uint32_t client_tokens_interval,
> +                                                     uint32_t self_tokens,
> +                                                     SpiceCharDeviceCallbacks *cbs,
> +                                                     void *opaque);
> +
> +void spice_char_device_state_reset_dev_instance(SpiceCharDeviceState *dev,
> +                                                SpiceCharDeviceInstance *sin);
> +void spice_char_device_state_destroy(SpiceCharDeviceState *dev);
> +
> +void *spice_char_device_state_opaque_get(SpiceCharDeviceState *dev);
> +
> +/* only one client is supported */
> +void spice_char_device_state_migrate_data_marshall(SpiceCharDeviceState *dev,
> +                                                  SpiceMarshaller *m);
> +void spice_char_device_state_migrate_data_marshall_empty(SpiceMarshaller *m);
> +
> +int spice_char_device_state_restore(SpiceCharDeviceState *dev,
> +                                    SpiceMigrateDataCharDevice *mig_data);
> +
> +/*
> + * Resets write/read queues, and moves that state to being stopped.
> + * This routine is a workaround for a bad tokens management in the vdagent
> + * protocol:
> + *  The client tokens' are set only once, when the main channel is initialized.
> + *  Instead, it would have been more appropriate to reset them upon AGEN_CONNECT.
> + *  The client tokens are tracked as part of the SpiceCharDeviceClientState. Thus,
> + *  in order to be backwartd compatible with the client, we need to track the tokens
> + *  event when the agent is detached. We don't destroy the char_device state, and
> + *  instead we just reset it.
> + *  In addition, there is a misshandling of AGENT_TOKENS message in spice-gtk: it
> + *  overrides the amount of tokens, instead of adding the given amount.
> + *
> + *  todo: change AGENT_CONNECT msg to contain tokens count.
> + */
> +void spice_char_device_reset(SpiceCharDeviceState *dev);
> +
> +/* max_send_queue_size = how many messages we can read from the device and enqueue for this client,
> + * when we have tokens for other clients and no tokens for this one */
> +int spice_char_device_client_add(SpiceCharDeviceState *dev,
> +                                 RedClient *client,
> +                                 int do_flow_control,
> +                                 uint32_t max_send_queue_size,
> +                                 uint32_t num_client_tokens,
> +                                 uint32_t num_send_tokens,
> +                                 int wait_for_migrate_data);
> +
> +void spice_char_device_client_remove(SpiceCharDeviceState *dev,
> +                                     RedClient *client);
> +int spice_char_device_client_exists(SpiceCharDeviceState *dev,
> +                                    RedClient *client);
> +
> +void spice_char_device_start(SpiceCharDeviceState *dev);
> +void spice_char_device_stop(SpiceCharDeviceState *dev);
> +
> +/** Read from device **/
> +
> +void spice_char_device_wakeup(SpiceCharDeviceState *dev);
> +
> +void spice_char_device_send_to_client_tokens_add(SpiceCharDeviceState *dev,
> +                                                 RedClient *client,
> +                                                 uint32_t tokens);
> +
> +
> +void spice_char_device_send_to_client_tokens_set(SpiceCharDeviceState *dev,
> +                                                 RedClient *client,
> +                                                 uint32_t tokens);
> +/** Write to device **/
> +
> +SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get(SpiceCharDeviceState *dev,
> +                                                               RedClient *client, int size);
> +SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get_server_no_token(
> +    SpiceCharDeviceState *dev, int size);
> +
> +/* Either add the buffer to the write queue or release it */
> +void spice_char_device_write_buffer_add(SpiceCharDeviceState *dev,
> +                                        SpiceCharDeviceWriteBuffer *write_buf);
> +void spice_char_device_write_buffer_release(SpiceCharDeviceState *dev,
> +                                            SpiceCharDeviceWriteBuffer *write_buf);
> +
> +/* api for specific char devices */
> +
> +SpiceCharDeviceState *spicevmc_device_connect(SpiceCharDeviceInstance *sin,
> +                                              uint8_t channel_type);
> +void spicevmc_device_disconnect(SpiceCharDeviceInstance *char_device);
> +
> +#endif // CHAR_DEVICE_H_
> diff --git a/server/char_device.c b/server/char_device.c
> deleted file mode 100644
> index ae7cb98..0000000
> --- a/server/char_device.c
> +++ /dev/null
> @@ -1,1035 +0,0 @@
> -/* spice-server char device flow control code
> -
> -   Copyright (C) 2012 Red Hat, Inc.
> -
> -   Red Hat Authors:
> -   Yonit Halperin <yhalperi at redhat.com>
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http:www.gnu.org/licenses/>.
> -*/
> -
> -
> -#include <config.h>
> -#include "char_device.h"
> -#include "red_channel.h"
> -#include "reds.h"
> -
> -#define CHAR_DEVICE_WRITE_TO_TIMEOUT 100
> -#define SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT 30000
> -#define MAX_POOL_SIZE (10 * 64 * 1024)
> -
> -typedef struct SpiceCharDeviceClientState SpiceCharDeviceClientState;
> -struct SpiceCharDeviceClientState {
> -    RingItem link;
> -    SpiceCharDeviceState *dev;
> -    RedClient *client;
> -    int do_flow_control;
> -    uint64_t num_client_tokens;
> -    uint64_t num_client_tokens_free; /* client messages that were consumed by the device */
> -    uint64_t num_send_tokens; /* send to client */
> -    SpiceTimer *wait_for_tokens_timer;
> -    int wait_for_tokens_started;
> -    Ring send_queue;
> -    uint32_t send_queue_size;
> -    uint32_t max_send_queue_size;
> -};
> -
> -struct SpiceCharDeviceState {
> -    int running;
> -    int active; /* has read/write been performed since the device was started */
> -    int wait_for_migrate_data;
> -    uint32_t refs;
> -
> -    Ring write_queue;
> -    Ring write_bufs_pool;
> -    uint64_t cur_pool_size;
> -    SpiceCharDeviceWriteBuffer *cur_write_buf;
> -    uint8_t *cur_write_buf_pos;
> -    SpiceTimer *write_to_dev_timer;
> -    uint64_t num_self_tokens;
> -
> -    Ring clients; /* list of SpiceCharDeviceClientState */
> -    uint32_t num_clients;
> -
> -    uint64_t client_tokens_interval; /* frequency of returning tokens to the client */
> -    SpiceCharDeviceInstance *sin;
> -
> -    int during_read_from_device;
> -    int during_write_to_device;
> -
> -    SpiceCharDeviceCallbacks cbs;
> -    void *opaque;
> -};
> -
> -enum {
> -    WRITE_BUFFER_ORIGIN_NONE,
> -    WRITE_BUFFER_ORIGIN_CLIENT,
> -    WRITE_BUFFER_ORIGIN_SERVER,
> -    WRITE_BUFFER_ORIGIN_SERVER_NO_TOKEN,
> -};
> -
> -/* Holding references for avoiding access violation if the char device was
> - * destroyed during a callback */
> -static void spice_char_device_state_ref(SpiceCharDeviceState *char_dev);
> -static void spice_char_device_state_unref(SpiceCharDeviceState *char_dev);
> -static void spice_char_device_write_buffer_unref(SpiceCharDeviceWriteBuffer *write_buf);
> -
> -static void spice_char_dev_write_retry(void *opaque);
> -
> -typedef struct SpiceCharDeviceMsgToClientItem {
> -    RingItem link;
> -    SpiceCharDeviceMsgToClient *msg;
> -} SpiceCharDeviceMsgToClientItem;
> -
> -static void spice_char_device_write_buffer_free(SpiceCharDeviceWriteBuffer *buf)
> -{
> -    if (buf == NULL)
> -        return;
> -
> -    free(buf->buf);
> -    free(buf);
> -}
> -
> -static void write_buffers_queue_free(Ring *write_queue)
> -{
> -    while (!ring_is_empty(write_queue)) {
> -        RingItem *item = ring_get_tail(write_queue);
> -        SpiceCharDeviceWriteBuffer *buf;
> -
> -        ring_remove(item);
> -        buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
> -        spice_char_device_write_buffer_free(buf);
> -    }
> -}
> -
> -static void spice_char_device_write_buffer_pool_add(SpiceCharDeviceState *dev,
> -                                                    SpiceCharDeviceWriteBuffer *buf)
> -{
> -    if (buf->refs == 1 &&
> -        dev->cur_pool_size < MAX_POOL_SIZE) {
> -        buf->buf_used = 0;
> -        buf->origin = WRITE_BUFFER_ORIGIN_NONE;
> -        buf->client = NULL;
> -        dev->cur_pool_size += buf->buf_size;
> -        ring_add(&dev->write_bufs_pool, &buf->link);
> -        return;
> -    }
> -
> -    /* Buffer still being used - just unref for the caller */
> -    spice_char_device_write_buffer_unref(buf);
> -}
> -
> -static void spice_char_device_client_send_queue_free(SpiceCharDeviceState *dev,
> -                                                     SpiceCharDeviceClientState *dev_client)
> -{
> -    spice_debug("send_queue_empty %d", ring_is_empty(&dev_client->send_queue));
> -    while (!ring_is_empty(&dev_client->send_queue)) {
> -        RingItem *item = ring_get_tail(&dev_client->send_queue);
> -        SpiceCharDeviceMsgToClientItem *msg_item = SPICE_CONTAINEROF(item,
> -                                                                     SpiceCharDeviceMsgToClientItem,
> -                                                                     link);
> -
> -        ring_remove(item);
> -        dev->cbs.unref_msg_to_client(msg_item->msg, dev->opaque);
> -        free(msg_item);
> -    }
> -    dev_client->num_send_tokens += dev_client->send_queue_size;
> -    dev_client->send_queue_size = 0;
> -}
> -
> -static void spice_char_device_client_free(SpiceCharDeviceState *dev,
> -                                          SpiceCharDeviceClientState *dev_client)
> -{
> -    RingItem *item, *next;
> -
> -    if (dev_client->wait_for_tokens_timer) {
> -        core->timer_remove(dev_client->wait_for_tokens_timer);
> -    }
> -
> -    spice_char_device_client_send_queue_free(dev, dev_client);
> -
> -    /* remove write buffers that are associated with the client */
> -    spice_debug("write_queue_is_empty %d", ring_is_empty(&dev->write_queue) && !dev->cur_write_buf);
> -    RING_FOREACH_SAFE(item, next, &dev->write_queue) {
> -        SpiceCharDeviceWriteBuffer *write_buf;
> -
> -        write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
> -        if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
> -            write_buf->client == dev_client->client) {
> -            ring_remove(item);
> -            spice_char_device_write_buffer_pool_add(dev, write_buf);
> -        }
> -    }
> -
> -    if (dev->cur_write_buf && dev->cur_write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
> -        dev->cur_write_buf->client == dev_client->client) {
> -        dev->cur_write_buf->origin = WRITE_BUFFER_ORIGIN_NONE;
> -        dev->cur_write_buf->client = NULL;
> -    }
> -
> -    dev->num_clients--;
> -    ring_remove(&dev_client->link);
> -    free(dev_client);
> -}
> -
> -static void spice_char_device_handle_client_overflow(SpiceCharDeviceClientState *dev_client)
> -{
> -    SpiceCharDeviceState *dev = dev_client->dev;
> -    spice_printerr("dev %p client %p ", dev, dev_client);
> -    dev->cbs.remove_client(dev_client->client, dev->opaque);
> -}
> -
> -static SpiceCharDeviceClientState *spice_char_device_client_find(SpiceCharDeviceState *dev,
> -                                                                 RedClient *client)
> -{
> -    RingItem *item;
> -
> -    RING_FOREACH(item, &dev->clients) {
> -        SpiceCharDeviceClientState *dev_client;
> -
> -        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
> -        if (dev_client->client == client) {
> -            return dev_client;
> -        }
> -    }
> -    return NULL;
> -}
> -
> -/***************************
> - * Reading from the device *
> - **************************/
> -
> -static void device_client_wait_for_tokens_timeout(void *opaque)
> -{
> -    SpiceCharDeviceClientState *dev_client = opaque;
> -
> -    spice_char_device_handle_client_overflow(dev_client);
> -}
> -
> -static int spice_char_device_can_send_to_client(SpiceCharDeviceClientState *dev_client)
> -{
> -    return !dev_client->do_flow_control || dev_client->num_send_tokens;
> -}
> -
> -static uint64_t spice_char_device_max_send_tokens(SpiceCharDeviceState *dev)
> -{
> -    RingItem *item;
> -    uint64_t max = 0;
> -
> -    RING_FOREACH(item, &dev->clients) {
> -        SpiceCharDeviceClientState *dev_client;
> -
> -        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
> -
> -        if (!dev_client->do_flow_control) {
> -            max = ~0;
> -            break;
> -        }
> -
> -        if (dev_client->num_send_tokens > max) {
> -            max = dev_client->num_send_tokens;
> -        }
> -    }
> -    return max;
> -}
> -
> -static void spice_char_device_add_msg_to_client_queue(SpiceCharDeviceClientState *dev_client,
> -                                                      SpiceCharDeviceMsgToClient *msg)
> -{
> -    SpiceCharDeviceState *dev = dev_client->dev;
> -    SpiceCharDeviceMsgToClientItem *msg_item;
> -
> -    if (dev_client->send_queue_size >= dev_client->max_send_queue_size) {
> -        spice_char_device_handle_client_overflow(dev_client);
> -        return;
> -    }
> -
> -    msg_item = spice_new0(SpiceCharDeviceMsgToClientItem, 1);
> -    msg_item->msg = dev->cbs.ref_msg_to_client(msg, dev->opaque);
> -    ring_add(&dev_client->send_queue, &msg_item->link);
> -    dev_client->send_queue_size++;
> -    if (!dev_client->wait_for_tokens_started) {
> -        core->timer_start(dev_client->wait_for_tokens_timer,
> -                          SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT);
> -        dev_client->wait_for_tokens_started = TRUE;
> -    }
> -}
> -
> -static void spice_char_device_send_msg_to_clients(SpiceCharDeviceState *dev,
> -                                                  SpiceCharDeviceMsgToClient *msg)
> -{
> -    RingItem *item, *next;
> -
> -    RING_FOREACH_SAFE(item, next, &dev->clients) {
> -        SpiceCharDeviceClientState *dev_client;
> -
> -        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
> -        if (spice_char_device_can_send_to_client(dev_client)) {
> -            dev_client->num_send_tokens--;
> -            spice_assert(ring_is_empty(&dev_client->send_queue));
> -            dev->cbs.send_msg_to_client(msg, dev_client->client, dev->opaque);
> -
> -            /* don't refer to dev_client anymore, it may have been released */
> -        } else {
> -            spice_char_device_add_msg_to_client_queue(dev_client, msg);
> -        }
> -    }
> -}
> -
> -static int spice_char_device_read_from_device(SpiceCharDeviceState *dev)
> -{
> -    uint64_t max_send_tokens;
> -    int did_read = FALSE;
> -
> -    if (!dev->running || dev->wait_for_migrate_data || !dev->sin) {
> -        return FALSE;
> -    }
> -
> -    /* There are 2 scenarios where we can get called recursively:
> -     * 1) spice-vmc vmc_read triggering flush of throttled data, recalling wakeup
> -     * (virtio)
> -     * 2) in case of sending messages to the client, and unreferencing the
> -     * msg, we trigger another read.
> -     */
> -    if (dev->during_read_from_device++ > 0) {
> -        return FALSE;
> -    }
> -
> -    max_send_tokens = spice_char_device_max_send_tokens(dev);
> -    spice_char_device_state_ref(dev);
> -    /*
> -     * Reading from the device only in case at least one of the clients have a free token.
> -     * All messages will be discarded if no client is attached to the device
> -     */
> -    while ((max_send_tokens || ring_is_empty(&dev->clients)) && dev->running) {
> -        SpiceCharDeviceMsgToClient *msg;
> -
> -        msg = dev->cbs.read_one_msg_from_device(dev->sin, dev->opaque);
> -        if (!msg) {
> -            if (dev->during_read_from_device > 1) {
> -                dev->during_read_from_device = 1;
> -                continue; /* a wakeup might have been called during the read -
> -                             make sure it doesn't get lost */
> -            }
> -            break;
> -        }
> -        did_read = TRUE;
> -        spice_char_device_send_msg_to_clients(dev, msg);
> -        dev->cbs.unref_msg_to_client(msg, dev->opaque);
> -        max_send_tokens--;
> -    }
> -    dev->during_read_from_device = 0;
> -    if (dev->running) {
> -        dev->active = dev->active || did_read;
> -    }
> -    spice_char_device_state_unref(dev);
> -    return did_read;
> -}
> -
> -static void spice_char_device_client_send_queue_push(SpiceCharDeviceClientState *dev_client)
> -{
> -    RingItem *item;
> -    while ((item = ring_get_tail(&dev_client->send_queue)) &&
> -           spice_char_device_can_send_to_client(dev_client)) {
> -        SpiceCharDeviceMsgToClientItem *msg_item;
> -
> -        msg_item = SPICE_CONTAINEROF(item, SpiceCharDeviceMsgToClientItem, link);
> -        ring_remove(item);
> -
> -        dev_client->num_send_tokens--;
> -        dev_client->dev->cbs.send_msg_to_client(msg_item->msg,
> -                                           dev_client->client,
> -                                           dev_client->dev->opaque);
> -        dev_client->dev->cbs.unref_msg_to_client(msg_item->msg, dev_client->dev->opaque);
> -        dev_client->send_queue_size--;
> -        free(msg_item);
> -    }
> -}
> -
> -static void spice_char_device_send_to_client_tokens_absorb(SpiceCharDeviceClientState *dev_client,
> -                                                           uint32_t tokens)
> -{
> -    dev_client->num_send_tokens += tokens;
> -
> -    if (dev_client->send_queue_size) {
> -        spice_assert(dev_client->num_send_tokens == tokens);
> -        spice_char_device_client_send_queue_push(dev_client);
> -    }
> -
> -    if (spice_char_device_can_send_to_client(dev_client)) {
> -        core->timer_cancel(dev_client->wait_for_tokens_timer);
> -        dev_client->wait_for_tokens_started = FALSE;
> -        spice_char_device_read_from_device(dev_client->dev);
> -    } else if (dev_client->send_queue_size) {
> -        core->timer_start(dev_client->wait_for_tokens_timer,
> -                          SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT);
> -        dev_client->wait_for_tokens_started = TRUE;
> -    }
> -}
> -
> -void spice_char_device_send_to_client_tokens_add(SpiceCharDeviceState *dev,
> -                                                 RedClient *client,
> -                                                 uint32_t tokens)
> -{
> -    SpiceCharDeviceClientState *dev_client;
> -
> -    dev_client = spice_char_device_client_find(dev, client);
> -
> -    if (!dev_client) {
> -        spice_error("client wasn't found dev %p client %p", dev, client);
> -        return;
> -    }
> -    spice_char_device_send_to_client_tokens_absorb(dev_client, tokens);
> -}
> -
> -void spice_char_device_send_to_client_tokens_set(SpiceCharDeviceState *dev,
> -                                                 RedClient *client,
> -                                                 uint32_t tokens)
> -{
> -    SpiceCharDeviceClientState *dev_client;
> -
> -    dev_client = spice_char_device_client_find(dev, client);
> -
> -    if (!dev_client) {
> -        spice_error("client wasn't found dev %p client %p", dev, client);
> -        return;
> -    }
> -
> -    dev_client->num_send_tokens = 0;
> -    spice_char_device_send_to_client_tokens_absorb(dev_client, tokens);
> -}
> -
> -/**************************
> - * Writing to the device  *
> -***************************/
> -
> -static void spice_char_device_client_tokens_add(SpiceCharDeviceState *dev,
> -                                                SpiceCharDeviceClientState *dev_client,
> -                                                uint32_t num_tokens)
> -{
> -    if (!dev_client->do_flow_control) {
> -        return;
> -    }
> -    if (num_tokens > 1) {
> -        spice_debug("#tokens > 1 (=%u)", num_tokens);
> -    }
> -    dev_client->num_client_tokens_free += num_tokens;
> -    if (dev_client->num_client_tokens_free >= dev->client_tokens_interval) {
> -        uint32_t tokens = dev_client->num_client_tokens_free;
> -
> -        dev_client->num_client_tokens += dev_client->num_client_tokens_free;
> -        dev_client->num_client_tokens_free = 0;
> -        dev->cbs.send_tokens_to_client(dev_client->client,
> -                                       tokens,
> -                                       dev->opaque);
> -    }
> -}
> -
> -static int spice_char_device_write_to_device(SpiceCharDeviceState *dev)
> -{
> -    SpiceCharDeviceInterface *sif;
> -    int total = 0;
> -    int n;
> -
> -    if (!dev->running || dev->wait_for_migrate_data || !dev->sin) {
> -        return 0;
> -    }
> -
> -    /* protect against recursion with spice_char_device_wakeup */
> -    if (dev->during_write_to_device++ > 0) {
> -        return 0;
> -    }
> -
> -    spice_char_device_state_ref(dev);
> -
> -    if (dev->write_to_dev_timer) {
> -        core->timer_cancel(dev->write_to_dev_timer);
> -    }
> -
> -    sif = SPICE_CONTAINEROF(dev->sin->base.sif, SpiceCharDeviceInterface, base);
> -    while (dev->running) {
> -        uint32_t write_len;
> -
> -        if (!dev->cur_write_buf) {
> -            RingItem *item = ring_get_tail(&dev->write_queue);
> -            if (!item) {
> -                break;
> -            }
> -            dev->cur_write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
> -            dev->cur_write_buf_pos = dev->cur_write_buf->buf;
> -            ring_remove(item);
> -        }
> -
> -        write_len = dev->cur_write_buf->buf + dev->cur_write_buf->buf_used -
> -                    dev->cur_write_buf_pos;
> -        n = sif->write(dev->sin, dev->cur_write_buf_pos, write_len);
> -        if (n <= 0) {
> -            if (dev->during_write_to_device > 1) {
> -                dev->during_write_to_device = 1;
> -                continue; /* a wakeup might have been called during the write -
> -                             make sure it doesn't get lost */
> -            }
> -            break;
> -        }
> -        total += n;
> -        write_len -= n;
> -        if (!write_len) {
> -            SpiceCharDeviceWriteBuffer *release_buf = dev->cur_write_buf;
> -            dev->cur_write_buf = NULL;
> -            spice_char_device_write_buffer_release(dev, release_buf);
> -            continue;
> -        }
> -        dev->cur_write_buf_pos += n;
> -    }
> -    /* retry writing as long as the write queue is not empty */
> -    if (dev->running) {
> -        if (dev->cur_write_buf) {
> -            if (dev->write_to_dev_timer) {
> -                core->timer_start(dev->write_to_dev_timer,
> -                                  CHAR_DEVICE_WRITE_TO_TIMEOUT);
> -            }
> -        } else {
> -            spice_assert(ring_is_empty(&dev->write_queue));
> -        }
> -        dev->active = dev->active || total;
> -    }
> -    dev->during_write_to_device = 0;
> -    spice_char_device_state_unref(dev);
> -    return total;
> -}
> -
> -static void spice_char_dev_write_retry(void *opaque)
> -{
> -    SpiceCharDeviceState *dev = opaque;
> -
> -    if (dev->write_to_dev_timer) {
> -        core->timer_cancel(dev->write_to_dev_timer);
> -    }
> -    spice_char_device_write_to_device(dev);
> -}
> -
> -static SpiceCharDeviceWriteBuffer *__spice_char_device_write_buffer_get(
> -    SpiceCharDeviceState *dev, RedClient *client,
> -    int size, int origin, int migrated_data_tokens)
> -{
> -    RingItem *item;
> -    SpiceCharDeviceWriteBuffer *ret;
> -
> -    if (origin == WRITE_BUFFER_ORIGIN_SERVER && !dev->num_self_tokens) {
> -        return NULL;
> -    }
> -
> -    if ((item = ring_get_tail(&dev->write_bufs_pool))) {
> -        ret = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
> -        ring_remove(item);
> -        dev->cur_pool_size -= ret->buf_size;
> -    } else {
> -        ret = spice_new0(SpiceCharDeviceWriteBuffer, 1);
> -    }
> -
> -    spice_assert(!ret->buf_used);
> -
> -    if (ret->buf_size < size) {
> -        ret->buf = spice_realloc(ret->buf, size);
> -        ret->buf_size = size;
> -    }
> -    ret->origin = origin;
> -
> -    if (origin == WRITE_BUFFER_ORIGIN_CLIENT) {
> -       spice_assert(client);
> -       SpiceCharDeviceClientState *dev_client = spice_char_device_client_find(dev, client);
> -       if (dev_client) {
> -            if (!migrated_data_tokens &&
> -                dev_client->do_flow_control && !dev_client->num_client_tokens) {
> -                spice_printerr("token violation: dev %p client %p", dev, client);
> -                spice_char_device_handle_client_overflow(dev_client);
> -                goto error;
> -            }
> -            ret->client = client;
> -            if (!migrated_data_tokens && dev_client->do_flow_control) {
> -                dev_client->num_client_tokens--;
> -            }
> -        } else {
> -            /* it is possible that the client was removed due to send tokens underflow, but
> -             * the caller still receive messages from the client */
> -            spice_printerr("client not found: dev %p client %p", dev, client);
> -            goto error;
> -        }
> -    } else if (origin == WRITE_BUFFER_ORIGIN_SERVER) {
> -        dev->num_self_tokens--;
> -    }
> -
> -    ret->token_price = migrated_data_tokens ? migrated_data_tokens : 1;
> -    ret->refs = 1;
> -    return ret;
> -error:
> -    dev->cur_pool_size += ret->buf_size;
> -    ring_add(&dev->write_bufs_pool, &ret->link);
> -    return NULL;
> -}
> -
> -SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get(SpiceCharDeviceState *dev,
> -                                                               RedClient *client,
> -                                                               int size)
> -{
> -   return  __spice_char_device_write_buffer_get(dev, client, size,
> -             client ? WRITE_BUFFER_ORIGIN_CLIENT : WRITE_BUFFER_ORIGIN_SERVER,
> -             0);
> -}
> -
> -SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get_server_no_token(
> -    SpiceCharDeviceState *dev, int size)
> -{
> -   return  __spice_char_device_write_buffer_get(dev, NULL, size,
> -             WRITE_BUFFER_ORIGIN_SERVER_NO_TOKEN, 0);
> -}
> -
> -static SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_ref(SpiceCharDeviceWriteBuffer *write_buf)
> -{
> -    spice_assert(write_buf);
> -
> -    write_buf->refs++;
> -    return write_buf;
> -}
> -
> -static void spice_char_device_write_buffer_unref(SpiceCharDeviceWriteBuffer *write_buf)
> -{
> -    spice_assert(write_buf);
> -
> -    write_buf->refs--;
> -    if (write_buf->refs == 0)
> -        spice_char_device_write_buffer_free(write_buf);
> -}
> -
> -void spice_char_device_write_buffer_add(SpiceCharDeviceState *dev,
> -                                        SpiceCharDeviceWriteBuffer *write_buf)
> -{
> -    spice_assert(dev);
> -    /* caller shouldn't add buffers for client that was removed */
> -    if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
> -        !spice_char_device_client_find(dev, write_buf->client)) {
> -        spice_printerr("client not found: dev %p client %p", dev, write_buf->client);
> -        spice_char_device_write_buffer_pool_add(dev, write_buf);
> -        return;
> -    }
> -
> -    ring_add(&dev->write_queue, &write_buf->link);
> -    spice_char_device_write_to_device(dev);
> -}
> -
> -void spice_char_device_write_buffer_release(SpiceCharDeviceState *dev,
> -                                            SpiceCharDeviceWriteBuffer *write_buf)
> -{
> -    int buf_origin = write_buf->origin;
> -    uint32_t buf_token_price = write_buf->token_price;
> -    RedClient *client = write_buf->client;
> -
> -    spice_assert(!ring_item_is_linked(&write_buf->link));
> -    if (!dev) {
> -        spice_printerr("no device. write buffer is freed");
> -        spice_char_device_write_buffer_free(write_buf);
> -        return;
> -    }
> -
> -    spice_assert(dev->cur_write_buf != write_buf);
> -
> -    spice_char_device_write_buffer_pool_add(dev, write_buf);
> -    if (buf_origin == WRITE_BUFFER_ORIGIN_CLIENT) {
> -        SpiceCharDeviceClientState *dev_client;
> -
> -        spice_assert(client);
> -        dev_client = spice_char_device_client_find(dev, client);
> -        /* when a client is removed, we remove all the buffers that are associated with it */
> -        spice_assert(dev_client);
> -        spice_char_device_client_tokens_add(dev, dev_client, buf_token_price);
> -    } else if (buf_origin == WRITE_BUFFER_ORIGIN_SERVER) {
> -        dev->num_self_tokens++;
> -        if (dev->cbs.on_free_self_token) {
> -            dev->cbs.on_free_self_token(dev->opaque);
> -        }
> -    }
> -}
> -
> -/********************************
> - * char_device_state management *
> - ********************************/
> -
> -SpiceCharDeviceState *spice_char_device_state_create(SpiceCharDeviceInstance *sin,
> -                                                     uint32_t client_tokens_interval,
> -                                                     uint32_t self_tokens,
> -                                                     SpiceCharDeviceCallbacks *cbs,
> -                                                     void *opaque)
> -{
> -    SpiceCharDeviceState *char_dev;
> -    SpiceCharDeviceInterface *sif;
> -
> -    spice_assert(sin);
> -    spice_assert(cbs->read_one_msg_from_device && cbs->ref_msg_to_client &&
> -                 cbs->unref_msg_to_client && cbs->send_msg_to_client &&
> -                 cbs->send_tokens_to_client && cbs->remove_client);
> -
> -    char_dev = spice_new0(SpiceCharDeviceState, 1);
> -    char_dev->sin = sin;
> -    char_dev->cbs = *cbs;
> -    char_dev->opaque = opaque;
> -    char_dev->client_tokens_interval = client_tokens_interval;
> -    char_dev->num_self_tokens = self_tokens;
> -
> -    ring_init(&char_dev->write_queue);
> -    ring_init(&char_dev->write_bufs_pool);
> -    ring_init(&char_dev->clients);
> -
> -    sif = SPICE_CONTAINEROF(char_dev->sin->base.sif, SpiceCharDeviceInterface, base);
> -    if (sif->base.minor_version <= 2 ||
> -        !(sif->flags & SPICE_CHAR_DEVICE_NOTIFY_WRITABLE)) {
> -        char_dev->write_to_dev_timer = core->timer_add(spice_char_dev_write_retry, char_dev);
> -        if (!char_dev->write_to_dev_timer) {
> -            spice_error("failed creating char dev write timer");
> -        }
> -    }
> -
> -    char_dev->refs = 1;
> -    sin->st = char_dev;
> -    spice_debug("sin %p dev_state %p", sin, char_dev);
> -    return char_dev;
> -}
> -
> -void spice_char_device_state_reset_dev_instance(SpiceCharDeviceState *state,
> -                                                SpiceCharDeviceInstance *sin)
> -{
> -    spice_debug("sin %p dev_state %p", sin, state);
> -    state->sin = sin;
> -    sin->st = state;
> -}
> -
> -void *spice_char_device_state_opaque_get(SpiceCharDeviceState *dev)
> -{
> -    return dev->opaque;
> -}
> -
> -static void spice_char_device_state_ref(SpiceCharDeviceState *char_dev)
> -{
> -    char_dev->refs++;
> -}
> -
> -static void spice_char_device_state_unref(SpiceCharDeviceState *char_dev)
> -{
> -    /* The refs field protects the char_dev from being deallocated in
> -     * case spice_char_device_state_destroy has been called
> -     * during a callabck, and we might still access the char_dev afterwards.
> -     * spice_char_device_state_unref is always coupled with a preceding
> -     * spice_char_device_state_ref. Here, refs can turn 0
> -     * only when spice_char_device_state_destroy is called in between
> -     * the calls to spice_char_device_state_ref and spice_char_device_state_unref.*/
> -    if (!--char_dev->refs) {
> -        free(char_dev);
> -    }
> -}
> -
> -void spice_char_device_state_destroy(SpiceCharDeviceState *char_dev)
> -{
> -    reds_on_char_device_state_destroy(char_dev);
> -    if (char_dev->write_to_dev_timer) {
> -        core->timer_remove(char_dev->write_to_dev_timer);
> -        char_dev->write_to_dev_timer = NULL;
> -    }
> -    write_buffers_queue_free(&char_dev->write_queue);
> -    write_buffers_queue_free(&char_dev->write_bufs_pool);
> -    char_dev->cur_pool_size = 0;
> -    spice_char_device_write_buffer_free(char_dev->cur_write_buf);
> -    char_dev->cur_write_buf = NULL;
> -
> -    while (!ring_is_empty(&char_dev->clients)) {
> -        RingItem *item = ring_get_tail(&char_dev->clients);
> -        SpiceCharDeviceClientState *dev_client;
> -
> -        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
> -        spice_char_device_client_free(char_dev, dev_client);
> -    }
> -    char_dev->running = FALSE;
> -
> -    spice_char_device_state_unref(char_dev);
> -}
> -
> -int spice_char_device_client_add(SpiceCharDeviceState *dev,
> -                                 RedClient *client,
> -                                 int do_flow_control,
> -                                 uint32_t max_send_queue_size,
> -                                 uint32_t num_client_tokens,
> -                                 uint32_t num_send_tokens,
> -                                 int wait_for_migrate_data)
> -{
> -    SpiceCharDeviceClientState *dev_client;
> -
> -    spice_assert(dev);
> -    spice_assert(client);
> -
> -    if (wait_for_migrate_data && (dev->num_clients > 0 || dev->active)) {
> -        spice_warning("can't restore device %p from migration data. The device "
> -                      "has already been active", dev);
> -        return FALSE;
> -    }
> -
> -    dev->wait_for_migrate_data = wait_for_migrate_data;
> -
> -    spice_debug("dev_state %p client %p", dev, client);
> -    dev_client = spice_new0(SpiceCharDeviceClientState, 1);
> -    dev_client->dev = dev;
> -    dev_client->client = client;
> -    ring_init(&dev_client->send_queue);
> -    dev_client->send_queue_size = 0;
> -    dev_client->max_send_queue_size = max_send_queue_size;
> -    dev_client->do_flow_control = do_flow_control;
> -    if (do_flow_control) {
> -        dev_client->wait_for_tokens_timer = core->timer_add(device_client_wait_for_tokens_timeout,
> -                                                            dev_client);
> -        if (!dev_client->wait_for_tokens_timer) {
> -            spice_error("failed to create wait for tokens timer");
> -        }
> -        dev_client->num_client_tokens = num_client_tokens;
> -        dev_client->num_send_tokens = num_send_tokens;
> -    } else {
> -        dev_client->num_client_tokens = ~0;
> -        dev_client->num_send_tokens = ~0;
> -    }
> -    ring_add(&dev->clients, &dev_client->link);
> -    dev->num_clients++;
> -    /* Now that we have a client, forward any pending device data */
> -    spice_char_device_wakeup(dev);
> -    return TRUE;
> -}
> -
> -void spice_char_device_client_remove(SpiceCharDeviceState *dev,
> -                                     RedClient *client)
> -{
> -    SpiceCharDeviceClientState *dev_client;
> -
> -    spice_debug("dev_state %p client %p", dev, client);
> -    dev_client = spice_char_device_client_find(dev, client);
> -
> -    if (!dev_client) {
> -        spice_error("client wasn't found");
> -        return;
> -    }
> -    spice_char_device_client_free(dev, dev_client);
> -    if (dev->wait_for_migrate_data) {
> -        spice_assert(dev->num_clients == 0);
> -        dev->wait_for_migrate_data  = FALSE;
> -        spice_char_device_read_from_device(dev);
> -    }
> -
> -    if (dev->num_clients == 0) {
> -        spice_debug("client removed, memory pool will be freed (%lu bytes)", dev->cur_pool_size);
> -        write_buffers_queue_free(&dev->write_bufs_pool);
> -        dev->cur_pool_size = 0;
> -    }
> -}
> -
> -int spice_char_device_client_exists(SpiceCharDeviceState *dev,
> -                                    RedClient *client)
> -{
> -    return (spice_char_device_client_find(dev, client) != NULL);
> -}
> -
> -void spice_char_device_start(SpiceCharDeviceState *dev)
> -{
> -    spice_debug("dev_state %p", dev);
> -    dev->running = TRUE;
> -    spice_char_device_state_ref(dev);
> -    while (spice_char_device_write_to_device(dev) ||
> -           spice_char_device_read_from_device(dev));
> -    spice_char_device_state_unref(dev);
> -}
> -
> -void spice_char_device_stop(SpiceCharDeviceState *dev)
> -{
> -    spice_debug("dev_state %p", dev);
> -    dev->running = FALSE;
> -    dev->active = FALSE;
> -    if (dev->write_to_dev_timer) {
> -        core->timer_cancel(dev->write_to_dev_timer);
> -    }
> -}
> -
> -void spice_char_device_reset(SpiceCharDeviceState *dev)
> -{
> -    RingItem *client_item;
> -
> -    spice_char_device_stop(dev);
> -    dev->wait_for_migrate_data = FALSE;
> -    spice_debug("dev_state %p", dev);
> -    while (!ring_is_empty(&dev->write_queue)) {
> -        RingItem *item = ring_get_tail(&dev->write_queue);
> -        SpiceCharDeviceWriteBuffer *buf;
> -
> -        ring_remove(item);
> -        buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
> -        /* tracking the tokens */
> -        spice_char_device_write_buffer_release(dev, buf);
> -    }
> -    if (dev->cur_write_buf) {
> -        SpiceCharDeviceWriteBuffer *release_buf = dev->cur_write_buf;
> -
> -        dev->cur_write_buf = NULL;
> -        spice_char_device_write_buffer_release(dev, release_buf);
> -    }
> -
> -    RING_FOREACH(client_item, &dev->clients) {
> -        SpiceCharDeviceClientState *dev_client;
> -
> -        dev_client = SPICE_CONTAINEROF(client_item, SpiceCharDeviceClientState, link);
> -        spice_char_device_client_send_queue_free(dev, dev_client);
> -    }
> -    dev->sin = NULL;
> -}
> -
> -void spice_char_device_wakeup(SpiceCharDeviceState *dev)
> -{
> -    spice_char_device_write_to_device(dev);
> -    spice_char_device_read_from_device(dev);
> -}
> -
> -/*************
> - * Migration *
> - * **********/
> -
> -void spice_char_device_state_migrate_data_marshall_empty(SpiceMarshaller *m)
> -{
> -    SpiceMigrateDataCharDevice *mig_data;
> -
> -    spice_debug(NULL);
> -    mig_data = (SpiceMigrateDataCharDevice *)spice_marshaller_reserve_space(m,
> -                                                                            sizeof(*mig_data));
> -    memset(mig_data, 0, sizeof(*mig_data));
> -    mig_data->version = SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION;
> -    mig_data->connected = FALSE;
> -}
> -
> -static void migrate_data_marshaller_write_buffer_free(uint8_t *data, void *opaque)
> -{
> -    SpiceCharDeviceWriteBuffer *write_buf = (SpiceCharDeviceWriteBuffer *)opaque;
> -
> -    spice_char_device_write_buffer_unref(write_buf);
> -}
> -
> -void spice_char_device_state_migrate_data_marshall(SpiceCharDeviceState *dev,
> -                                                   SpiceMarshaller *m)
> -{
> -    SpiceCharDeviceClientState *client_state;
> -    RingItem *item;
> -    uint32_t *write_to_dev_size_ptr;
> -    uint32_t *write_to_dev_tokens_ptr;
> -    SpiceMarshaller *m2;
> -
> -    /* multi-clients are not supported */
> -    spice_assert(dev->num_clients == 1);
> -    client_state = SPICE_CONTAINEROF(ring_get_tail(&dev->clients),
> -                                     SpiceCharDeviceClientState,
> -                                     link);
> -    /* FIXME: if there were more than one client before the marshalling,
> -     * it is possible that the send_queue_size > 0, and the send data
> -     * should be migrated as well */
> -    spice_assert(client_state->send_queue_size == 0);
> -    spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION);
> -    spice_marshaller_add_uint8(m, 1); /* connected */
> -    spice_marshaller_add_uint32(m, client_state->num_client_tokens);
> -    spice_marshaller_add_uint32(m, client_state->num_send_tokens);
> -    write_to_dev_size_ptr = (uint32_t *)spice_marshaller_reserve_space(m, sizeof(uint32_t));
> -    write_to_dev_tokens_ptr = (uint32_t *)spice_marshaller_reserve_space(m, sizeof(uint32_t));
> -    *write_to_dev_size_ptr = 0;
> -    *write_to_dev_tokens_ptr = 0;
> -
> -    m2 = spice_marshaller_get_ptr_submarshaller(m, 0);
> -    if (dev->cur_write_buf) {
> -        uint32_t buf_remaining = dev->cur_write_buf->buf + dev->cur_write_buf->buf_used -
> -                                 dev->cur_write_buf_pos;
> -        spice_marshaller_add_ref_full(m2, dev->cur_write_buf_pos, buf_remaining,
> -                                      migrate_data_marshaller_write_buffer_free,
> -                                      spice_char_device_write_buffer_ref(dev->cur_write_buf)
> -                                      );
> -        *write_to_dev_size_ptr += buf_remaining;
> -        if (dev->cur_write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT) {
> -            spice_assert(dev->cur_write_buf->client == client_state->client);
> -            (*write_to_dev_tokens_ptr) += dev->cur_write_buf->token_price;
> -        }
> -    }
> -
> -    RING_FOREACH_REVERSED(item, &dev->write_queue) {
> -        SpiceCharDeviceWriteBuffer *write_buf;
> -
> -        write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
> -        spice_marshaller_add_ref_full(m2, write_buf->buf, write_buf->buf_used,
> -                                      migrate_data_marshaller_write_buffer_free,
> -                                      spice_char_device_write_buffer_ref(write_buf)
> -                                      );
> -        *write_to_dev_size_ptr += write_buf->buf_used;
> -        if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT) {
> -            spice_assert(write_buf->client == client_state->client);
> -            (*write_to_dev_tokens_ptr) += write_buf->token_price;
> -        }
> -    }
> -    spice_debug("migration data dev %p: write_queue size %u tokens %u",
> -                dev, *write_to_dev_size_ptr, *write_to_dev_tokens_ptr);
> -}
> -
> -int spice_char_device_state_restore(SpiceCharDeviceState *dev,
> -                                    SpiceMigrateDataCharDevice *mig_data)
> -{
> -    SpiceCharDeviceClientState *client_state;
> -    uint32_t client_tokens_window;
> -
> -    spice_assert(dev->num_clients == 1 && dev->wait_for_migrate_data);
> -
> -    client_state = SPICE_CONTAINEROF(ring_get_tail(&dev->clients),
> -                                     SpiceCharDeviceClientState,
> -                                     link);
> -    if (mig_data->version > SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION) {
> -        spice_error("dev %p error: migration data version %u is bigger than self %u",
> -                    dev, mig_data->version, SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION);
> -        return FALSE;
> -    }
> -    spice_assert(!dev->cur_write_buf && ring_is_empty(&dev->write_queue));
> -    spice_assert(mig_data->connected);
> -
> -    client_tokens_window = client_state->num_client_tokens; /* initial state of tokens */
> -    client_state->num_client_tokens = mig_data->num_client_tokens;
> -    /* assumption: client_tokens_window stays the same across severs */
> -    client_state->num_client_tokens_free = client_tokens_window -
> -                                           mig_data->num_client_tokens -
> -                                           mig_data->write_num_client_tokens;
> -    client_state->num_send_tokens = mig_data->num_send_tokens;
> -
> -    if (mig_data->write_size > 0) {
> -        if (mig_data->write_num_client_tokens) {
> -            dev->cur_write_buf =
> -                __spice_char_device_write_buffer_get(dev, client_state->client,
> -                    mig_data->write_size, WRITE_BUFFER_ORIGIN_CLIENT,
> -                    mig_data->write_num_client_tokens);
> -        } else {
> -            dev->cur_write_buf =
> -                __spice_char_device_write_buffer_get(dev, NULL,
> -                    mig_data->write_size, WRITE_BUFFER_ORIGIN_SERVER, 0);
> -        }
> -        /* the first write buffer contains all the data that was saved for migration */
> -        memcpy(dev->cur_write_buf->buf,
> -               ((uint8_t *)mig_data) + mig_data->write_data_ptr - sizeof(SpiceMigrateDataHeader),
> -               mig_data->write_size);
> -        dev->cur_write_buf->buf_used = mig_data->write_size;
> -        dev->cur_write_buf_pos = dev->cur_write_buf->buf;
> -    }
> -    dev->wait_for_migrate_data = FALSE;
> -    spice_char_device_write_to_device(dev);
> -    spice_char_device_read_from_device(dev);
> -    return TRUE;
> -}
> diff --git a/server/char_device.h b/server/char_device.h
> deleted file mode 100644
> index 55d1ee6..0000000
> --- a/server/char_device.h
> +++ /dev/null
> @@ -1,216 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009-2015 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifndef __CHAR_DEVICE_H__
> -#define __CHAR_DEVICE_H__
> -
> -#include "spice.h"
> -#include "red_channel.h"
> -#include "migration_protocol.h"
> -
> -/*
> - * Shared code for char devices, mainly for flow control.
> - *
> - * How to use the api:
> - * ==================
> - * device attached: call spice_char_device_state_create
> - * device detached: call spice_char_device_state_destroy/reset
> - *
> - * client connected and associated with a device: spice_char_device_client_add
> - * client disconnected: spice_char_device_client_remove
> - *
> - * Writing to the device
> - * ---------------------
> - * Write the data into SpiceCharDeviceWriteBuffer:
> - * call spice_char_device_write_buffer_get in order to get an appropriate buffer.
> - * call spice_char_device_write_buffer_add in order to push the buffer to the write queue.
> - * If you choose not to push the buffer to the device, call
> - * spice_char_device_write_buffer_release
> - *
> - * reading from the device
> - * -----------------------
> - *  The callback read_one_msg_from_device (see below) should be implemented
> - *  (using sif->read).
> - *  When the device is ready, this callback is called, and is expected to
> - *  return one message which is addressed to the client, or NULL if the read
> - *  hasn't completed.
> - *
> - * calls triggered from the device (qemu):
> - * --------------------------------------
> - * spice_char_device_start
> - * spice_char_device_stop
> - * spice_char_device_wakeup (for reading from the device)
> - */
> -
> -/*
> - * Note about multiple-clients:
> - * Multiclients are currently not supported in any of the character devices:
> - * spicevmc does not allow more than one client (and at least for usb, it should stay this way).
> - * smartcard code is not compatible with more than one reader.
> - * The server and guest agent code doesn't distinguish messages from different clients.
> - * In addition, its current flow control code (e.g., tokens handling) is wrong and doesn't
> - * take into account the different clients.
> - *
> - * Nonetheless, the following code introduces some support for multiple-clients:
> - * We track the number of tokens for all the clients, and we read from the device
> - * if one of the clients have enough tokens. For the clients that don't have tokens,
> - * we queue the messages, till they receive tokens, or till a timeout.
> - *
> - * TODO:
> - * At least for the agent, not all the messages from the device will be directed to all
> - * the clients (e.g., copy from guest to a specific client). Thus, support for
> - * client-specific-messages should be added.
> - * In addition, we should have support for clients that are being connected
> - * in the middle of a message transfer from the agent to the clients.
> - *
> - * */
> -
> -/* buffer that is used for writing to the device */
> -typedef struct SpiceCharDeviceWriteBuffer {
> -    RingItem link;
> -    int origin;
> -    RedClient *client; /* The client that sent the message to the device.
> -                          NULL if the server created the message */
> -
> -    uint8_t *buf;
> -    uint32_t buf_size;
> -    uint32_t buf_used;
> -    uint32_t token_price;
> -    uint32_t refs;
> -} SpiceCharDeviceWriteBuffer;
> -
> -typedef void SpiceCharDeviceMsgToClient;
> -
> -typedef struct SpiceCharDeviceCallbacks {
> -    /*
> -     * Messages that are addressed to the client can be queued in case we have
> -     * multiple clients and some of them don't have enough tokens.
> -     */
> -
> -    /* reads from the device till reaching a msg that should be sent to the client,
> -     * or till the reading fails */
> -    SpiceCharDeviceMsgToClient* (*read_one_msg_from_device)(SpiceCharDeviceInstance *sin,
> -                                                            void *opaque);
> -    SpiceCharDeviceMsgToClient* (*ref_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
> -                                                     void *opaque);
> -    void (*unref_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
> -                                void *opaque);
> -    void (*send_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
> -                               RedClient *client,
> -                               void *opaque); /* after this call, the message is unreferenced */
> -
> -    /* The cb is called when a predefined number of write buffers were consumed by the
> -     * device */
> -    void (*send_tokens_to_client)(RedClient *client, uint32_t tokens, void *opaque);
> -
> -    /* The cb is called when a server (self) message that was addressed to the device,
> -     * has been completely written to it */
> -    void (*on_free_self_token)(void *opaque);
> -
> -    /* This cb is called if it is recommanded that a client will be removed
> -     * due to slow flow or due to some other error.
> -     * The called instance should disconnect the client, or at least the corresponding channel */
> -    void (*remove_client)(RedClient *client, void *opaque);
> -} SpiceCharDeviceCallbacks;
> -
> -SpiceCharDeviceState *spice_char_device_state_create(SpiceCharDeviceInstance *sin,
> -                                                     uint32_t client_tokens_interval,
> -                                                     uint32_t self_tokens,
> -                                                     SpiceCharDeviceCallbacks *cbs,
> -                                                     void *opaque);
> -
> -void spice_char_device_state_reset_dev_instance(SpiceCharDeviceState *dev,
> -                                                SpiceCharDeviceInstance *sin);
> -void spice_char_device_state_destroy(SpiceCharDeviceState *dev);
> -
> -void *spice_char_device_state_opaque_get(SpiceCharDeviceState *dev);
> -
> -/* only one client is supported */
> -void spice_char_device_state_migrate_data_marshall(SpiceCharDeviceState *dev,
> -                                                  SpiceMarshaller *m);
> -void spice_char_device_state_migrate_data_marshall_empty(SpiceMarshaller *m);
> -
> -int spice_char_device_state_restore(SpiceCharDeviceState *dev,
> -                                    SpiceMigrateDataCharDevice *mig_data);
> -
> -/*
> - * Resets write/read queues, and moves that state to being stopped.
> - * This routine is a workaround for a bad tokens management in the vdagent
> - * protocol:
> - *  The client tokens' are set only once, when the main channel is initialized.
> - *  Instead, it would have been more appropriate to reset them upon AGEN_CONNECT.
> - *  The client tokens are tracked as part of the SpiceCharDeviceClientState. Thus,
> - *  in order to be backwartd compatible with the client, we need to track the tokens
> - *  event when the agent is detached. We don't destroy the char_device state, and
> - *  instead we just reset it.
> - *  In addition, there is a misshandling of AGENT_TOKENS message in spice-gtk: it
> - *  overrides the amount of tokens, instead of adding the given amount.
> - *
> - *  todo: change AGENT_CONNECT msg to contain tokens count.
> - */
> -void spice_char_device_reset(SpiceCharDeviceState *dev);
> -
> -/* max_send_queue_size = how many messages we can read from the device and enqueue for this client,
> - * when we have tokens for other clients and no tokens for this one */
> -int spice_char_device_client_add(SpiceCharDeviceState *dev,
> -                                 RedClient *client,
> -                                 int do_flow_control,
> -                                 uint32_t max_send_queue_size,
> -                                 uint32_t num_client_tokens,
> -                                 uint32_t num_send_tokens,
> -                                 int wait_for_migrate_data);
> -
> -void spice_char_device_client_remove(SpiceCharDeviceState *dev,
> -                                     RedClient *client);
> -int spice_char_device_client_exists(SpiceCharDeviceState *dev,
> -                                    RedClient *client);
> -
> -void spice_char_device_start(SpiceCharDeviceState *dev);
> -void spice_char_device_stop(SpiceCharDeviceState *dev);
> -
> -/** Read from device **/
> -
> -void spice_char_device_wakeup(SpiceCharDeviceState *dev);
> -
> -void spice_char_device_send_to_client_tokens_add(SpiceCharDeviceState *dev,
> -                                                 RedClient *client,
> -                                                 uint32_t tokens);
> -
> -
> -void spice_char_device_send_to_client_tokens_set(SpiceCharDeviceState *dev,
> -                                                 RedClient *client,
> -                                                 uint32_t tokens);
> -/** Write to device **/
> -
> -SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get(SpiceCharDeviceState *dev,
> -                                                               RedClient *client, int size);
> -SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get_server_no_token(
> -    SpiceCharDeviceState *dev, int size);
> -
> -/* Either add the buffer to the write queue or release it */
> -void spice_char_device_write_buffer_add(SpiceCharDeviceState *dev,
> -                                        SpiceCharDeviceWriteBuffer *write_buf);
> -void spice_char_device_write_buffer_release(SpiceCharDeviceState *dev,
> -                                            SpiceCharDeviceWriteBuffer *write_buf);
> -
> -/* api for specific char devices */
> -
> -SpiceCharDeviceState *spicevmc_device_connect(SpiceCharDeviceInstance *sin,
> -                                              uint8_t channel_type);
> -void spicevmc_device_disconnect(SpiceCharDeviceInstance *char_device);
> -
> -#endif // __CHAR_DEVICE_H__
> diff --git a/server/dcc-encoders.h b/server/dcc-encoders.h
> index 5de66f7..a0a5c64 100644
> --- a/server/dcc-encoders.h
> +++ b/server/dcc-encoders.h
> @@ -23,14 +23,14 @@
>  #include "common/quic.h"
>  #include "red_channel.h"
>  #include "red_parse_qxl.h"
> -#include "spice_image_cache.h"
> -#include "glz_encoder_dictionary.h"
> -#include "glz_encoder.h"
> -#include "jpeg_encoder.h"
> +#include "image-cache.h"
> +#include "glz-encoder-dict.h"
> +#include "glz-encoder.h"
> +#include "jpeg-encoder.h"
>  #ifdef USE_LZ4
>  #include "lz4_encoder.h"
>  #endif
> -#include "zlib_encoder.h"
> +#include "zlib-encoder.h"
>
>  typedef struct RedCompressBuf RedCompressBuf;
>  typedef struct GlzDrawableInstanceItem GlzDrawableInstanceItem;
> diff --git a/server/display-channel.h b/server/display-channel.h
> index a990e09..50f6c5e 100644
> --- a/server/display-channel.h
> +++ b/server/display-channel.h
> @@ -25,22 +25,22 @@
>  #include "reds_stream.h"
>  #include "cache-item.h"
>  #include "pixmap-cache.h"
> -#include "reds_sw_canvas.h"
> +#include "sw-canvas.h"
>  #include "stat.h"
>  #include "reds.h"
> -#include "mjpeg_encoder.h"
> -#include "red_memslots.h"
> +#include "mjpeg-encoder.h"
> +#include "memslot.h"
>  #include "red_parse_qxl.h"
>  #include "red_record_qxl.h"
>  #include "demarshallers.h"
>  #include "red_channel.h"
>  #include "red_dispatcher.h"
>  #include "dispatcher.h"
> -#include "main_channel.h"
> -#include "migration_protocol.h"
> -#include "main_dispatcher.h"
> +#include "main-channel.h"
> +#include "migration-protocol.h"
> +#include "main-dispatcher.h"
>  #include "spice_bitmap_utils.h"
> -#include "spice_image_cache.h"
> +#include "image-cache.h"
>  #include "utils.h"
>  #include "tree.h"
>  #include "stream.h"
> diff --git a/server/glz-encoder-dict.c b/server/glz-encoder-dict.c
> new file mode 100644
> index 0000000..1fd6753
> --- /dev/null
> +++ b/server/glz-encoder-dict.c
> @@ -0,0 +1,633 @@
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <pthread.h>
> +#include <string.h>
> +#include <stdio.h>
> +
> +#include "glz-encoder-dict.h"
> +#include "glz-encoder-priv.h"
> +
> +/* turning all used images to free ones. If they are alive, calling the free_image callback for
> +   each one */
> +static inline void __glz_dictionary_window_reset_images(SharedDictionary *dict)
> +{
> +    WindowImage *tmp;
> +
> +    while (dict->window.used_images_head) {
> +        tmp = dict->window.used_images_head;
> +        dict->window.used_images_head = dict->window.used_images_head->next;
> +        if (tmp->is_alive) {
> +            dict->cur_usr->free_image(dict->cur_usr, tmp->usr_context);
> +        }
> +        tmp->next = dict->window.free_images;
> +        tmp->is_alive = FALSE;
> +        dict->window.free_images = tmp;
> +    }
> +    dict->window.used_images_tail = NULL;
> +}
> +
> +/* allocate window fields (no reset)*/
> +static int glz_dictionary_window_create(SharedDictionary *dict, uint32_t size)
> +{
> +    if (size > LZ_MAX_WINDOW_SIZE) {
> +        return FALSE;
> +    }
> +
> +    dict->window.size_limit = size;
> +    dict->window.segs = (WindowImageSegment *)(
> +            dict->cur_usr->malloc(dict->cur_usr, sizeof(WindowImageSegment) * INIT_IMAGE_SEGS_NUM));
> +
> +    if (!dict->window.segs) {
> +        return FALSE;
> +    }
> +
> +    dict->window.segs_quota = INIT_IMAGE_SEGS_NUM;
> +
> +    dict->window.encoders_heads = (uint32_t *)dict->cur_usr->malloc(dict->cur_usr,
> +                                                            sizeof(uint32_t) * dict->max_encoders);
> +
> +    if (!dict->window.encoders_heads) {
> +        dict->cur_usr->free(dict->cur_usr, dict->window.segs);
> +        return FALSE;
> +    }
> +
> +    dict->window.used_images_head = NULL;
> +    dict->window.used_images_tail = NULL;
> +    dict->window.free_images = NULL;
> +    dict->window.pixels_so_far = 0;
> +
> +    return TRUE;
> +}
> +
> +/* initializes an empty window (segs and encoder_heads should be pre allocated.
> +   resets the image infos, and calls the free_image usr callback*/
> +static void glz_dictionary_window_reset(SharedDictionary *dict)
> +{
> +    uint32_t i;
> +    WindowImageSegment *seg, *last_seg;
> +
> +    last_seg = dict->window.segs + dict->window.segs_quota;
> +    /* reset free segs list */
> +    dict->window.free_segs_head = 0;
> +    for (seg = dict->window.segs, i = 0; seg < last_seg; seg++, i++) {
> +        seg->next = i + 1;
> +        seg->image = NULL;
> +        seg->lines = NULL;
> +        seg->lines_end = NULL;
> +        seg->pixels_num = 0;
> +        seg->pixels_so_far = 0;
> +    }
> +    dict->window.segs[dict->window.segs_quota - 1].next = NULL_IMAGE_SEG_ID;
> +
> +    dict->window.used_segs_head = NULL_IMAGE_SEG_ID;
> +    dict->window.used_segs_tail = NULL_IMAGE_SEG_ID;
> +
> +    // reset encoders heads
> +    for (i = 0; i < dict->max_encoders; i++) {
> +        dict->window.encoders_heads[i] = NULL_IMAGE_SEG_ID;
> +    }
> +
> +    __glz_dictionary_window_reset_images(dict);
> +}
> +
> +static inline void glz_dictionary_reset_hash(SharedDictionary *dict)
> +{
> +    memset(dict->htab, 0, sizeof(HashEntry) * HASH_SIZE * HASH_CHAIN_SIZE);
> +#ifdef CHAINED_HASH
> +    memset(dict->htab_counter, 0, HASH_SIZE * sizeof(uint8_t));
> +#endif
> +}
> +
> +static inline void glz_dictionary_window_destroy(SharedDictionary *dict)
> +{
> +    __glz_dictionary_window_reset_images(dict);
> +
> +    if (dict->window.segs) {
> +        dict->cur_usr->free(dict->cur_usr, dict->window.segs);
> +        dict->window.segs = NULL;
> +    }
> +
> +    while (dict->window.free_images) {
> +        WindowImage *tmp = dict->window.free_images;
> +        dict->window.free_images = tmp->next;
> +
> +        dict->cur_usr->free(dict->cur_usr, tmp);
> +    }
> +
> +    if (dict->window.encoders_heads) {
> +        dict->cur_usr->free(dict->cur_usr, dict->window.encoders_heads);
> +        dict->window.encoders_heads = NULL;
> +    }
> +}
> +
> +/* logic removal only */
> +static inline void glz_dictionary_window_kill_image(SharedDictionary *dict, WindowImage *image)
> +{
> +    image->is_alive = FALSE;
> +}
> +
> +GlzEncDictContext *glz_enc_dictionary_create(uint32_t size, uint32_t max_encoders,
> +                                             GlzEncoderUsrContext *usr)
> +{
> +    SharedDictionary *dict;
> +
> +    if (!(dict = (SharedDictionary *)usr->malloc(usr,
> +                                                 sizeof(SharedDictionary)))) {
> +        return NULL;
> +    }
> +
> +    dict->cur_usr = usr;
> +    dict->last_image_id = 0;
> +    dict->max_encoders = max_encoders;
> +
> +    pthread_mutex_init(&dict->lock, NULL);
> +    pthread_rwlock_init(&dict->rw_alloc_lock, NULL);
> +
> +    dict->window.encoders_heads = NULL;
> +
> +    // alloc window fields and reset
> +    if (!glz_dictionary_window_create(dict, size)) {
> +        dict->cur_usr->free(usr, dict);
> +        return NULL;
> +    }
> +
> +    // reset window and hash
> +    glz_enc_dictionary_reset((GlzEncDictContext *)dict, usr);
> +
> +    return (GlzEncDictContext *)dict;
> +}
> +
> +void glz_enc_dictionary_get_restore_data(GlzEncDictContext *opaque_dict,
> +                                         GlzEncDictRestoreData *out_data, GlzEncoderUsrContext *usr)
> +{
> +    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
> +    dict->cur_usr = usr;
> +    GLZ_ASSERT(dict->cur_usr, opaque_dict);
> +    GLZ_ASSERT(dict->cur_usr, out_data);
> +
> +    out_data->last_image_id = dict->last_image_id;
> +    out_data->max_encoders = dict->max_encoders;
> +    out_data->size = dict->window.size_limit;
> +}
> +
> +GlzEncDictContext *glz_enc_dictionary_restore(GlzEncDictRestoreData *restore_data,
> +                                              GlzEncoderUsrContext *usr)
> +{
> +    if (!restore_data) {
> +        return NULL;
> +    }
> +    SharedDictionary *ret = (SharedDictionary *)glz_enc_dictionary_create(
> +            restore_data->size, restore_data->max_encoders, usr);
> +    ret->last_image_id = restore_data->last_image_id;
> +    return ((GlzEncDictContext *)ret);
> +}
> +
> +void glz_enc_dictionary_reset(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr)
> +{
> +    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
> +    dict->cur_usr = usr;
> +    GLZ_ASSERT(dict->cur_usr, opaque_dict);
> +
> +    dict->last_image_id = 0;
> +    glz_dictionary_window_reset(dict);
> +    glz_dictionary_reset_hash(dict);
> +}
> +
> +void glz_enc_dictionary_destroy(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr)
> +{
> +    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
> +
> +    if (!opaque_dict) {
> +        return;
> +    }
> +
> +    dict->cur_usr = usr;
> +    glz_dictionary_window_destroy(dict);
> +
> +    pthread_mutex_destroy(&dict->lock);
> +    pthread_rwlock_destroy(&dict->rw_alloc_lock);
> +
> +    dict->cur_usr->free(dict->cur_usr, dict);
> +}
> +
> +uint32_t glz_enc_dictionary_get_size(GlzEncDictContext *opaque_dict)
> +{
> +    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
> +
> +    if (!opaque_dict) {
> +        return 0;
> +    }
> +    return dict->window.size_limit;
> +}
> +
> +/* doesn't call the remove image callback */
> +void glz_enc_dictionary_remove_image(GlzEncDictContext *opaque_dict,
> +                                     GlzEncDictImageContext *opaque_image,
> +                                     GlzEncoderUsrContext *usr)
> +{
> +    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
> +    WindowImage *image = (WindowImage *)opaque_image;
> +    dict->cur_usr = usr;
> +    GLZ_ASSERT(dict->cur_usr, opaque_image && opaque_dict);
> +
> +    glz_dictionary_window_kill_image(dict, image);
> +}
> +
> +/***********************************************************************************
> + Mutators of the window. Should be called by the encoder before and after encoding.
> + ***********************************************************************************/
> +
> +static inline int __get_pixels_num(LzImageType image_type, unsigned int num_lines, int stride)
> +{
> +    if (IS_IMAGE_TYPE_RGB[image_type]) {
> +        return num_lines * stride / RGB_BYTES_PER_PIXEL[image_type];
> +    } else {
> +        return num_lines * stride * PLT_PIXELS_PER_BYTE[image_type];
> +    }
> +}
> +
> +static void __glz_dictionary_window_segs_realloc(SharedDictionary *dict)
> +{
> +    WindowImageSegment *new_segs;
> +    uint32_t new_quota = (MAX_IMAGE_SEGS_NUM < (dict->window.segs_quota * 2)) ?
> +        MAX_IMAGE_SEGS_NUM : (dict->window.segs_quota * 2);
> +    WindowImageSegment *seg;
> +    uint32_t i;
> +
> +    pthread_rwlock_wrlock(&dict->rw_alloc_lock);
> +
> +    if (dict->window.segs_quota == MAX_IMAGE_SEGS_NUM) {
> +        dict->cur_usr->error(dict->cur_usr, "overflow in image segments window\n");
> +    }
> +
> +    new_segs = (WindowImageSegment*)dict->cur_usr->malloc(
> +            dict->cur_usr, sizeof(WindowImageSegment) * new_quota);
> +
> +    if (!new_segs) {
> +        dict->cur_usr->error(dict->cur_usr,
> +                             "realloc of dictionary window failed\n");
> +    }
> +
> +    memcpy(new_segs, dict->window.segs,
> +           sizeof(WindowImageSegment) * dict->window.segs_quota);
> +
> +    // resetting the new elements
> +    for (i = dict->window.segs_quota, seg = new_segs + i; i < new_quota; i++, seg++) {
> +        seg->image = NULL;
> +        seg->lines = NULL;
> +        seg->lines_end = NULL;
> +        seg->pixels_num = 0;
> +        seg->pixels_so_far = 0;
> +        seg->next = i + 1;
> +    }
> +    new_segs[new_quota - 1].next = dict->window.free_segs_head;
> +    dict->window.free_segs_head = dict->window.segs_quota;
> +
> +    dict->cur_usr->free(dict->cur_usr, dict->window.segs);
> +    dict->window.segs = new_segs;
> +    dict->window.segs_quota = new_quota;
> +
> +    pthread_rwlock_unlock(&dict->rw_alloc_lock);
> +}
> +
> +/* NOTE - it also updates the used_images_list*/
> +static WindowImage *__glz_dictionary_window_alloc_image(SharedDictionary *dict)
> +{
> +    WindowImage *ret;
> +
> +    if (dict->window.free_images) {
> +        ret = dict->window.free_images;
> +        dict->window.free_images = ret->next;
> +    } else {
> +        if (!(ret = (WindowImage *)dict->cur_usr->malloc(dict->cur_usr,
> +                                                         sizeof(*ret)))) {
> +            return NULL;
> +        }
> +    }
> +
> +    ret->next = NULL;
> +    if (dict->window.used_images_tail) {
> +        dict->window.used_images_tail->next = ret;
> +    }
> +    dict->window.used_images_tail = ret;
> +
> +    if (!dict->window.used_images_head) {
> +        dict->window.used_images_head = ret;
> +    }
> +    return ret;
> +}
> +
> +/* NOTE - it doesn't update the used_segs list*/
> +static uint32_t __glz_dictionary_window_alloc_image_seg(SharedDictionary *dict)
> +{
> +    uint32_t seg_id;
> +    WindowImageSegment *seg;
> +
> +    // TODO: when is it best to realloc? when full or when half full?
> +    if (dict->window.free_segs_head == NULL_IMAGE_SEG_ID) {
> +        __glz_dictionary_window_segs_realloc(dict);
> +    }
> +
> +    GLZ_ASSERT(dict->cur_usr, dict->window.free_segs_head != NULL_IMAGE_SEG_ID);
> +
> +    seg_id = dict->window.free_segs_head;
> +    seg = dict->window.segs + seg_id;
> +    dict->window.free_segs_head = seg->next;
> +
> +    return seg_id;
> +}
> +
> +/* moves image to free list and "kill" it. Calls the free_image callback if was alive. */
> +static inline void __glz_dictionary_window_free_image(SharedDictionary *dict, WindowImage *image)
> +{
> +    if (image->is_alive) {
> +        dict->cur_usr->free_image(dict->cur_usr, image->usr_context);
> +    }
> +    image->is_alive = FALSE;
> +    image->next = dict->window.free_images;
> +    dict->window.free_images = image;
> +}
> +
> +/* moves all the segments that were associated with the images to the free segments */
> +static inline void __glz_dictionary_window_free_image_segs(SharedDictionary *dict,
> +                                                           WindowImage *image)
> +{
> +    uint32_t old_free_head = dict->window.free_segs_head;
> +    uint32_t seg_id, next_seg_id;
> +
> +    GLZ_ASSERT(dict->cur_usr, image->first_seg != NULL_IMAGE_SEG_ID);
> +    dict->window.free_segs_head = image->first_seg;
> +
> +    // retrieving the last segment of the image
> +    for (seg_id = image->first_seg, next_seg_id = dict->window.segs[seg_id].next;
> +         (next_seg_id != NULL_IMAGE_SEG_ID) && (dict->window.segs[next_seg_id].image == image);
> +         seg_id = next_seg_id, next_seg_id = dict->window.segs[seg_id].next) {
> +    }
> +
> +    // concatenate the free list
> +    dict->window.segs[seg_id].next = old_free_head;
> +}
> +
> +/* Returns the logical head of the window after we add an image with the give size to its tail.
> +   Returns NULL when the window is empty, of when we have to empty the window in order
> +   to insert the new image. */
> +static WindowImage *glz_dictionary_window_get_new_head(SharedDictionary *dict, int new_image_size)
> +{
> +    uint32_t cur_win_size;
> +    WindowImage *cur_head;
> +
> +    if ((uint32_t)new_image_size > dict->window.size_limit) {
> +        dict->cur_usr->error(dict->cur_usr, "image is bigger than window\n");
> +    }
> +
> +    GLZ_ASSERT(dict->cur_usr, new_image_size < dict->window.size_limit)
> +
> +    // the window is empty
> +    if (!dict->window.used_images_head) {
> +        return NULL;
> +    }
> +
> +    GLZ_ASSERT(dict->cur_usr, dict->window.used_segs_head != NULL_IMAGE_SEG_ID);
> +    GLZ_ASSERT(dict->cur_usr, dict->window.used_segs_tail != NULL_IMAGE_SEG_ID);
> +
> +    // used_segs_head is the latest logical head (the physical head may preceed it)
> +    cur_head = dict->window.segs[dict->window.used_segs_head].image;
> +    cur_win_size = dict->window.segs[dict->window.used_segs_tail].pixels_num +
> +        dict->window.segs[dict->window.used_segs_tail].pixels_so_far -
> +        dict->window.segs[dict->window.used_segs_head].pixels_so_far;
> +
> +    while ((cur_win_size + new_image_size) > dict->window.size_limit) {
> +        GLZ_ASSERT(dict->cur_usr, cur_head);
> +        cur_win_size -= cur_head->size;
> +        cur_head = cur_head->next;
> +    }
> +
> +    return cur_head;
> +}
> +
> +static inline int glz_dictionary_is_in_use(SharedDictionary *dict)
> +{
> +    uint32_t i = 0;
> +    for (i = 0; i < dict->max_encoders; i++) {
> +        if (dict->window.encoders_heads[i] != NULL_IMAGE_SEG_ID) {
> +            return TRUE;
> +        }
> +    }
> +    return FALSE;
> +}
> +
> +/* remove from the window (and free relevant data) the images between the oldest physical head
> +   (inclusive) and the end_image (exclusive). If end_image is NULL, empties the window*/
> +static void glz_dictionary_window_remove_head(SharedDictionary *dict, uint32_t encoder_id,
> +                                              WindowImage *end_image)
> +{
> +    // note that the segs list heads (one per encoder) may be different than the
> +    // used_segs_head and it is updated somewhere else
> +    while (dict->window.used_images_head != end_image) {
> +        WindowImage *image = dict->window.used_images_head;
> +
> +        __glz_dictionary_window_free_image_segs(dict, image);
> +        dict->window.used_images_head = image->next;
> +        __glz_dictionary_window_free_image(dict, image);
> +    }
> +
> +    if (!dict->window.used_images_head) {
> +        dict->window.used_segs_head = NULL_IMAGE_SEG_ID;
> +        dict->window.used_segs_tail = NULL_IMAGE_SEG_ID;
> +        dict->window.used_images_tail = NULL;
> +    } else {
> +        dict->window.used_segs_head = end_image->first_seg;
> +    }
> +}
> +
> +static uint32_t glz_dictionary_window_alloc_image_seg(SharedDictionary *dict, WindowImage* image,
> +                                                      int size, int stride,
> +                                                      uint8_t *lines, unsigned int num_lines)
> +{
> +    uint32_t seg_id = __glz_dictionary_window_alloc_image_seg(dict);
> +    WindowImageSegment *seg = &dict->window.segs[seg_id];
> +
> +    seg->image = image;
> +    seg->lines = lines;
> +    seg->lines_end = lines + num_lines * stride;
> +    seg->pixels_num = size;
> +    seg->pixels_so_far = dict->window.pixels_so_far;
> +    dict->window.pixels_so_far += seg->pixels_num;
> +
> +    seg->next = NULL_IMAGE_SEG_ID;
> +
> +    return seg_id;
> +}
> +
> +static WindowImage *glz_dictionary_window_add_image(SharedDictionary *dict, LzImageType image_type,
> +                                                    int image_size, int image_height,
> +                                                    int image_stride, uint8_t *first_lines,
> +                                                    unsigned int num_first_lines,
> +                                                    GlzUsrImageContext *usr_image_context)
> +{
> +    unsigned int num_lines = num_first_lines;
> +    unsigned int row;
> +    uint32_t seg_id, prev_seg_id;
> +    uint8_t* lines = first_lines;
> +    // alloc image info,update used head tail,  if used_head null - update  head
> +    WindowImage *image = __glz_dictionary_window_alloc_image(dict);
> +    image->id = dict->last_image_id++;
> +    image->size = image_size;
> +    image->type = image_type;
> +    image->usr_context = usr_image_context;
> +
> +    if (num_lines <= 0) {
> +        num_lines = dict->cur_usr->more_lines(dict->cur_usr, &lines);
> +        if (num_lines <= 0) {
> +            dict->cur_usr->error(dict->cur_usr, "more lines failed\n");
> +        }
> +    }
> +
> +    for (row = 0;;) {
> +        seg_id = glz_dictionary_window_alloc_image_seg(dict, image,
> +                                                       image_size * num_lines / image_height,
> +                                                       image_stride,
> +                                                       lines, num_lines);
> +        if (row == 0) {
> +            image->first_seg = seg_id;
> +        } else {
> +            dict->window.segs[prev_seg_id].next = seg_id;
> +        }
> +
> +        row += num_lines;
> +        if (row < (uint32_t)image_height) {
> +            num_lines = dict->cur_usr->more_lines(dict->cur_usr, &lines);
> +            if (num_lines <= 0) {
> +                dict->cur_usr->error(dict->cur_usr, "more lines failed\n");
> +            }
> +        } else {
> +            break;
> +        }
> +        prev_seg_id = seg_id;
> +    }
> +
> +    if (dict->window.used_segs_tail == NULL_IMAGE_SEG_ID) {
> +        dict->window.used_segs_head = image->first_seg;
> +        dict->window.used_segs_tail = seg_id;
> +    } else {
> +        int prev_tail = dict->window.used_segs_tail;
> +
> +        // The used segs may be in use by another thread which is during encoding
> +        // (read-only use - when going over the segs of an image,
> +        // see glz_encode_tmpl::compress).
> +        // Thus, the 'next' field of the list's tail can be accessed only
> +        // after all the new tail's data was set. Note that we are relying on
> +        // an atomic assignment (32 bit variable).
> +        // For the other thread that may read 'next' of the old tail, NULL_IMAGE_SEG_ID
> +        // is equivalent to a segment with an image id that is different
> +        // from the image id of the tail, so we don't need to further protect this field.
> +        dict->window.segs[prev_tail].next = image->first_seg;
> +        dict->window.used_segs_tail = seg_id;
> +    }
> +    image->is_alive = TRUE;
> +
> +    return image;
> +}
> +
> +WindowImage *glz_dictionary_pre_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
> +                                       SharedDictionary *dict, LzImageType image_type,
> +                                       int image_width, int image_height, int image_stride,
> +                                       uint8_t *first_lines, unsigned int num_first_lines,
> +                                       GlzUsrImageContext *usr_image_context,
> +                                       uint32_t *image_head_dist)
> +{
> +    WindowImage *new_win_head, *ret;
> +    int image_size;
> +
> +
> +    pthread_mutex_lock(&dict->lock);
> +
> +    dict->cur_usr = usr;
> +    GLZ_ASSERT(dict->cur_usr, dict->window.encoders_heads[encoder_id] == NULL_IMAGE_SEG_ID);
> +
> +    image_size = __get_pixels_num(image_type, image_height, image_stride);
> +    new_win_head = glz_dictionary_window_get_new_head(dict, image_size);
> +
> +    if (!glz_dictionary_is_in_use(dict)) {
> +        glz_dictionary_window_remove_head(dict, encoder_id, new_win_head);
> +    }
> +
> +    ret = glz_dictionary_window_add_image(dict, image_type, image_size, image_height, image_stride,
> +                                          first_lines, num_first_lines, usr_image_context);
> +
> +    if (new_win_head) {
> +        dict->window.encoders_heads[encoder_id] = new_win_head->first_seg;
> +        *image_head_dist = (uint32_t)(ret->id - new_win_head->id); // shouldn't be greater than 32
> +                                                                   // bit because the window size is
> +                                                                   // limited to 2^25
> +    } else {
> +        dict->window.encoders_heads[encoder_id] = ret->first_seg;
> +        *image_head_dist = 0;
> +    }
> +
> +
> +    // update encoders head  (the other heads were already updated)
> +    pthread_mutex_unlock(&dict->lock);
> +    pthread_rwlock_rdlock(&dict->rw_alloc_lock);
> +    return ret;
> +}
> +
> +void glz_dictionary_post_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
> +                                SharedDictionary *dict)
> +{
> +    uint32_t i;
> +    uint32_t early_head_seg = NULL_IMAGE_SEG_ID;
> +    uint32_t this_encoder_head_seg;
> +
> +    pthread_rwlock_unlock(&dict->rw_alloc_lock);
> +    pthread_mutex_lock(&dict->lock);
> +    dict->cur_usr = usr;
> +
> +    GLZ_ASSERT(dict->cur_usr, dict->window.encoders_heads[encoder_id] != NULL_IMAGE_SEG_ID);
> +    // get the earliest head in use (not including this encoder head)
> +    for (i = 0; i < dict->max_encoders; i++) {
> +        if (i != encoder_id) {
> +            if (IMAGE_SEG_IS_EARLIER(dict, dict->window.encoders_heads[i], early_head_seg)) {
> +                early_head_seg = dict->window.encoders_heads[i];
> +            }
> +        }
> +    }
> +
> +    // possible only if early_head_seg == NULL
> +    if (IMAGE_SEG_IS_EARLIER(dict, dict->window.used_segs_head, early_head_seg)) {
> +        early_head_seg = dict->window.used_segs_head;
> +    }
> +
> +    this_encoder_head_seg = dict->window.encoders_heads[encoder_id];
> +
> +    GLZ_ASSERT(dict->cur_usr, early_head_seg != NULL_IMAGE_SEG_ID);
> +
> +    if (IMAGE_SEG_IS_EARLIER(dict, this_encoder_head_seg, early_head_seg)) {
> +        GLZ_ASSERT(dict->cur_usr,
> +                   this_encoder_head_seg == dict->window.used_images_head->first_seg);
> +        glz_dictionary_window_remove_head(dict, encoder_id,
> +                                          dict->window.segs[early_head_seg].image);
> +    }
> +
> +
> +    dict->window.encoders_heads[encoder_id] = NULL_IMAGE_SEG_ID;
> +    pthread_mutex_unlock(&dict->lock);
> +}
> diff --git a/server/glz-encoder-dict.h b/server/glz-encoder-dict.h
> new file mode 100644
> index 0000000..960f165
> --- /dev/null
> +++ b/server/glz-encoder-dict.h
> @@ -0,0 +1,69 @@
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +
> +#ifndef GLZ_ENCODER_DICT_H_
> +#define GLZ_ENCODER_DICT_H_
> +
> +#include <stdint.h>
> +#include "glz_encoder_config.h"
> +
> +/*
> +    Interface for maintaining lz dictionary that is shared among several encoders.
> +    The interface for accessing the dictionary for encoding purposes is located in
> +    glz-encoder-priv.h
> +*/
> +
> +typedef void GlzEncDictContext;
> +typedef void GlzEncDictImageContext;
> +
> +/* NOTE: DISPLAY_MIGRATE_DATA_VERSION should change in case GlzEncDictRestoreData changes*/
> +typedef struct GlzEncDictRestoreData {
> +    uint32_t size;
> +    uint32_t max_encoders;
> +    uint64_t last_image_id;
> +} GlzEncDictRestoreData;
> +
> +/* size        : maximal number of pixels occupying the window
> +   max_encoders: maximal number of encoders that use the dictionary
> +   usr         : callbacks */
> +GlzEncDictContext *glz_enc_dictionary_create(uint32_t size, uint32_t max_encoders,
> +                                             GlzEncoderUsrContext *usr);
> +
> +void glz_enc_dictionary_destroy(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr);
> +
> +/* returns the window capacity in pixels */
> +uint32_t glz_enc_dictionary_get_size(GlzEncDictContext *);
> +
> +/* returns the current state of the dictionary.
> +   NOTE - you should use it only when no encoder uses the dictionary. */
> +void glz_enc_dictionary_get_restore_data(GlzEncDictContext *opaque_dict,
> +                                         GlzEncDictRestoreData *out_data,
> +                                         GlzEncoderUsrContext *usr);
> +
> +/* creates a dictionary and initialized it by use the given info */
> +GlzEncDictContext *glz_enc_dictionary_restore(GlzEncDictRestoreData *restore_data,
> +                                              GlzEncoderUsrContext *usr);
> +
> +/*  NOTE - you should use this routine only when no encoder uses the dictionary. */
> +void glz_enc_dictionary_reset(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr);
> +
> +/* image: the context returned by the encoder when the image was encoded.
> +   NOTE - you should use this routine only when no encoder uses the dictionary.*/
> +void glz_enc_dictionary_remove_image(GlzEncDictContext *opaque_dict,
> +                                     GlzEncDictImageContext *image, GlzEncoderUsrContext *usr);
> +
> +#endif // GLZ_ENCODER_DICT_H_
> diff --git a/server/glz-encoder-priv.h b/server/glz-encoder-priv.h
> new file mode 100644
> index 0000000..a408966
> --- /dev/null
> +++ b/server/glz-encoder-priv.h
> @@ -0,0 +1,186 @@
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +
> +#ifndef GLZ_ENCODER_PRIV_H_
> +#define GLZ_ENCODER_PRIV_H_
> +
> +/* Interface for using the dictionary for encoding.
> +   Data structures are exposed for the encoder for efficiency
> +   purposes. */
> +typedef struct WindowImage WindowImage;
> +typedef struct WindowImageSegment WindowImageSegment;
> +
> +
> +//#define CHAINED_HASH
> +
> +#ifdef CHAINED_HASH
> +#define HASH_SIZE_LOG 16
> +#define HASH_CHAIN_SIZE 4
> +#else
> +#define HASH_SIZE_LOG 20
> +#define HASH_CHAIN_SIZE 1
> +#endif
> +
> +#define HASH_SIZE (1 << HASH_SIZE_LOG)
> +#define HASH_MASK (HASH_SIZE - 1)
> +
> +typedef struct HashEntry HashEntry;
> +
> +typedef struct SharedDictionary SharedDictionary;
> +
> +struct WindowImage {
> +    uint64_t id;
> +    LzImageType type;
> +    int size;                    // in pixels
> +    uint32_t first_seg;
> +    GlzUsrImageContext  *usr_context;
> +    WindowImage*       next;
> +    uint8_t is_alive;
> +};
> +
> +#define MAX_IMAGE_SEGS_NUM (0xffffffff)
> +#define NULL_IMAGE_SEG_ID MAX_IMAGE_SEGS_NUM
> +#define INIT_IMAGE_SEGS_NUM 1000
> +
> +/* Images can be separated into several chunks. The basic unit of the
> +   dictionary window is one image segment. Each segment is encoded separately.
> +   An encoded match can refer to only one segment.*/
> +struct WindowImageSegment {
> +    WindowImage     *image;
> +    void            *lines;
> +    void            *lines_end;
> +    uint32_t pixels_num;            // Number of pixels in the segment
> +    uint64_t pixels_so_far;         // Total no. pixels passed through the window till this segment.
> +                                    // NOTE - never use size delta independently. It should
> +                                    // always be used with respect to a previous size delta
> +    uint32_t next;
> +};
> +
> +
> +struct  __attribute__ ((__packed__)) HashEntry {
> +    uint32_t image_seg_idx;
> +    uint32_t ref_pix_idx;
> +};
> +
> +
> +struct SharedDictionary {
> +    struct {
> +        /* The segments storage. A dynamic array.
> +           By referring to a segment by its index, instead of address,
> +           we save space in the hash entries (32bit instead of 64bit) */
> +        WindowImageSegment  *segs;
> +        uint32_t segs_quota;
> +
> +        /* The window is manged as a linked list rather than as a cyclic
> +           array in order to keep the indices of the segments consistent
> +           after reallocation */
> +
> +        /* the window in a resolution of image segments */
> +        uint32_t used_segs_head;             // the latest head
> +        uint32_t used_segs_tail;
> +        uint32_t free_segs_head;
> +
> +        uint32_t            *encoders_heads; // Holds for each encoder (by id), the window head when
> +                                             // it started the encoding.
> +                                             // The head is NULL_IMAGE_SEG_ID when the encoder is
> +                                             // not encoding.
> +
> +        /* the window in a resolution of images. But here the head contains the oldest head*/
> +        WindowImage*        used_images_tail;
> +        WindowImage*        used_images_head;
> +        WindowImage*        free_images;
> +
> +        uint64_t pixels_so_far;
> +        uint32_t size_limit;                 // max number of pixels in a window (per encoder)
> +    } window;
> +
> +    /* Concurrency issues: the reading/writing of each entry field should be atomic.
> +       It is allowed that the reading/writing of the whole entry won't be atomic,
> +       since before we access a reference we check its validity*/
> +#ifdef CHAINED_HASH
> +    HashEntry htab[HASH_SIZE][HASH_CHAIN_SIZE];
> +    uint8_t htab_counter[HASH_SIZE];  //cyclic counter for the next entry in a chain to be assigned
> +#else
> +    HashEntry htab[HASH_SIZE];
> +#endif
> +
> +    uint64_t last_image_id;
> +    uint32_t max_encoders;
> +    pthread_mutex_t lock;
> +    pthread_rwlock_t rw_alloc_lock;
> +    GlzEncoderUsrContext       *cur_usr; // each encoder has other context.
> +};
> +
> +/*
> +    Add the image to the tail of the window.
> +    If possible, release images from the head of the window.
> +    Also perform concurrency related operations.
> +
> +    usr_image_context: when an image is released from the window due to capacity overflow,
> +                       usr_image_context is given as a parameter to the free_image callback.
> +
> +    image_head_dist  : the number of images between the current image and the head of the
> +                       window that is associated with the encoder.
> +*/
> +WindowImage *glz_dictionary_pre_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
> +                                       SharedDictionary *dict, LzImageType image_type,
> +                                       int image_width, int image_height, int image_stride,
> +                                       uint8_t *first_lines, unsigned int num_first_lines,
> +                                       GlzUsrImageContext *usr_image_context,
> +                                       uint32_t *image_head_dist);
> +
> +/*
> +   Performs concurrency related operations.
> +   If possible, release images from the head of the window.
> +*/
> +void glz_dictionary_post_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
> +                                SharedDictionary *dict);
> +
> +#define IMAGE_SEG_IS_EARLIER(dict, dst_seg, src_seg) (                     \
> +    ((src_seg) == NULL_IMAGE_SEG_ID) || (((dst_seg) != NULL_IMAGE_SEG_ID)  \
> +    && ((dict)->window.segs[(dst_seg)].pixels_so_far <                     \
> +       (dict)->window.segs[(src_seg)].pixels_so_far)))
> +
> +
> +#ifdef CHAINED_HASH
> +#define UPDATE_HASH(dict, hval, seg, pix) {                \
> +    uint8_t tmp_count = (dict)->htab_counter[hval];        \
> +    (dict)->htab[hval][tmp_count].image_seg_idx = seg;     \
> +    (dict)->htab[hval][tmp_count].ref_pix_idx = pix;       \
> +    tmp_count = ((tmp_count) + 1) & (HASH_CHAIN_SIZE - 1); \
> +    dict->htab_counter[hval] = tmp_count;                  \
> +}
> +#else
> +#define UPDATE_HASH(dict, hval, seg, pix) { \
> +    (dict)->htab[hval].image_seg_idx = seg; \
> +    (dict)->htab[hval].ref_pix_idx = pix;   \
> +}
> +#endif
> +
> +/* checks if the reference segment is located in the range of the window
> +   of the current encoder */
> +#define REF_SEG_IS_VALID(dict, enc_id, ref_seg, src_seg) ( \
> +    ((ref_seg) == (src_seg)) ||                            \
> +    ((ref_seg)->image &&                                   \
> +     (ref_seg)->image->is_alive &&                         \
> +     (src_seg->image->type == ref_seg->image->type) &&     \
> +     (ref_seg->pixels_so_far <= src_seg->pixels_so_far) && \
> +     ((dict)->window.segs[                                 \
> +        (dict)->window.encoders_heads[enc_id]].pixels_so_far <= \
> +        ref_seg->pixels_so_far)))
> +
> +#endif // GLZ_ENCODER_PRIV_H_
> diff --git a/server/glz-encoder.c b/server/glz-encoder.c
> new file mode 100644
> index 0000000..f761330
> --- /dev/null
> +++ b/server/glz-encoder.c
> @@ -0,0 +1,311 @@
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <glib.h>
> +#include <pthread.h>
> +#include <stdio.h>
> +#include "glz-encoder.h"
> +#include "glz-encoder-priv.h"
> +
> +
> +/* Holds a specific data for one encoder, and data that is relevant for the current image encoded */
> +typedef struct Encoder {
> +    GlzEncoderUsrContext *usr;
> +    uint8_t id;
> +    SharedDictionary     *dict;
> +
> +    struct {
> +        LzImageType type;
> +        uint32_t id;
> +        uint32_t first_win_seg;
> +    } cur_image;
> +
> +    struct {
> +        uint8_t            *start;
> +        uint8_t            *now;
> +        uint8_t            *end;
> +        size_t bytes_count;
> +        uint8_t            *last_copy;  // pointer to the last byte in which copy count was written
> +    } io;
> +} Encoder;
> +
> +
> +/**************************************************************************
> +* Handling writing the encoded image to the output buffer
> +***************************************************************************/
> +static inline int more_io_bytes(Encoder *encoder)
> +{
> +    uint8_t *io_ptr;
> +    int num_io_bytes = encoder->usr->more_space(encoder->usr, &io_ptr);
> +    encoder->io.bytes_count += num_io_bytes;
> +    encoder->io.now = io_ptr;
> +    encoder->io.end = encoder->io.now + num_io_bytes;
> +    return num_io_bytes;
> +}
> +
> +static inline void encode(Encoder *encoder, uint8_t byte)
> +{
> +    if (encoder->io.now == encoder->io.end) {
> +        if (more_io_bytes(encoder) <= 0) {
> +            encoder->usr->error(encoder->usr, "%s: no more bytes\n", __FUNCTION__);
> +        }
> +        GLZ_ASSERT(encoder->usr, encoder->io.now);
> +    }
> +
> +    GLZ_ASSERT(encoder->usr, encoder->io.now < encoder->io.end);
> +    *(encoder->io.now++) = byte;
> +}
> +
> +static inline void encode_32(Encoder *encoder, unsigned int word)
> +{
> +    encode(encoder, (uint8_t)(word >> 24));
> +    encode(encoder, (uint8_t)(word >> 16) & 0x0000ff);
> +    encode(encoder, (uint8_t)(word >> 8) & 0x0000ff);
> +    encode(encoder, (uint8_t)(word & 0x0000ff));
> +}
> +
> +static inline void encode_64(Encoder *encoder, uint64_t word)
> +{
> +    encode_32(encoder, (uint32_t)(word >> 32));
> +    encode_32(encoder, (uint32_t)(word & 0xffffff));
> +}
> +
> +static inline void encode_copy_count(Encoder *encoder, uint8_t copy_count)
> +{
> +    encode(encoder, copy_count);
> +    encoder->io.last_copy = encoder->io.now - 1; // io_now cannot be the first byte of the buffer
> +}
> +
> +static inline void update_copy_count(Encoder *encoder, uint8_t copy_count)
> +{
> +    GLZ_ASSERT(encoder->usr, encoder->io.last_copy);
> +    *(encoder->io.last_copy) = copy_count;
> +}
> +
> +// decrease the io ptr by 1
> +static inline void compress_output_prev(Encoder *encoder)
> +{
> +    // io_now cannot be the first byte of the buffer
> +    encoder->io.now--;
> +    // the function should be called only when copy count is written unnecessarily by glz_compress
> +    GLZ_ASSERT(encoder->usr, encoder->io.now == encoder->io.last_copy)
> +}
> +
> +static int encoder_reset(Encoder *encoder, uint8_t *io_ptr, uint8_t *io_ptr_end)
> +{
> +    GLZ_ASSERT(encoder->usr, io_ptr <= io_ptr_end);
> +    encoder->io.bytes_count = io_ptr_end - io_ptr;
> +    encoder->io.start = io_ptr;
> +    encoder->io.now = io_ptr;
> +    encoder->io.end = io_ptr_end;
> +    encoder->io.last_copy = NULL;
> +
> +    return TRUE;
> +}
> +
> +/**********************************************************
> +*           Encoding
> +***********************************************************/
> +
> +GlzEncoderContext *glz_encoder_create(uint8_t id, GlzEncDictContext *dictionary,
> +                                      GlzEncoderUsrContext *usr)
> +{
> +    Encoder *encoder;
> +
> +    if (!usr || !usr->error || !usr->warn || !usr->info || !usr->malloc ||
> +        !usr->free || !usr->more_space) {
> +        return NULL;
> +    }
> +
> +    if (!(encoder = (Encoder *)usr->malloc(usr, sizeof(Encoder)))) {
> +        return NULL;
> +    }
> +
> +    encoder->id = id;
> +    encoder->usr = usr;
> +    encoder->dict = (SharedDictionary *)dictionary;
> +
> +    return (GlzEncoderContext *)encoder;
> +}
> +
> +void glz_encoder_destroy(GlzEncoderContext *opaque_encoder)
> +{
> +    Encoder *encoder = (Encoder *)opaque_encoder;
> +
> +    if (!opaque_encoder) {
> +        return;
> +    }
> +
> +    encoder->usr->free(encoder->usr, encoder);
> +}
> +
> +/*
> + * Give hints to the compiler for branch prediction optimization.
> + */
> +#if defined(__GNUC__) && (__GNUC__ > 2)
> +#define LZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
> +#define LZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
> +#else
> +#define LZ_EXPECT_CONDITIONAL(c) (c)
> +#define LZ_UNEXPECT_CONDITIONAL(c) (c)
> +#endif
> +
> +
> +typedef uint8_t BYTE;
> +
> +typedef struct __attribute__ ((__packed__)) one_byte_pixel_t {
> +    BYTE a;
> +} one_byte_pixel_t;
> +
> +typedef struct __attribute__ ((__packed__)) rgb32_pixel_t {
> +    BYTE b;
> +    BYTE g;
> +    BYTE r;
> +    BYTE pad;
> +} rgb32_pixel_t;
> +
> +typedef struct __attribute__ ((__packed__)) rgb24_pixel_t {
> +    BYTE b;
> +    BYTE g;
> +    BYTE r;
> +} rgb24_pixel_t;
> +
> +typedef uint16_t rgb16_pixel_t;
> +
> +#define BOUND_OFFSET 2
> +#define LIMIT_OFFSET 6
> +#define MIN_FILE_SIZE 4
> +
> +#define MAX_PIXEL_SHORT_DISTANCE 4096       // (1 << 12)
> +#define MAX_PIXEL_MEDIUM_DISTANCE 131072    // (1 << 17)  2 ^ (12 + 5)
> +#define MAX_PIXEL_LONG_DISTANCE 33554432    // (1 << 25)  2 ^ (12 + 5 + 8)
> +#define MAX_IMAGE_DIST 16777215             // (1 << 24 - 1)
> +
> +
> +//#define DEBUG_ENCODE
> +
> +
> +#define GLZ_ENCODE_SIZE
> +#include "glz-encode-match.tmpl.c"
> +#define GLZ_ENCODE_MATCH
> +#include "glz-encode-match.tmpl.c"
> +
> +#define LZ_PLT
> +#include "glz-encode.tmpl.c"
> +
> +#define LZ_RGB16
> +#include "glz-encode.tmpl.c"
> +
> +#define LZ_RGB24
> +#include "glz-encode.tmpl.c"
> +
> +#define LZ_RGB32
> +#include "glz-encode.tmpl.c"
> +
> +#define LZ_RGB_ALPHA
> +#include "glz-encode.tmpl.c"
> +
> +
> +int glz_encode(GlzEncoderContext *opaque_encoder,
> +               LzImageType type, int width, int height, int top_down,
> +               uint8_t *lines, unsigned int num_lines, int stride,
> +               uint8_t *io_ptr, unsigned int num_io_bytes,
> +               GlzUsrImageContext *usr_context, GlzEncDictImageContext **o_enc_dict_context)
> +{
> +    Encoder *encoder = (Encoder *)opaque_encoder;
> +    WindowImage *dict_image;
> +    uint8_t *io_ptr_end = io_ptr + num_io_bytes;
> +    uint32_t win_head_image_dist;
> +
> +    if (IS_IMAGE_TYPE_PLT[type]) {
> +        if (stride > (width / PLT_PIXELS_PER_BYTE[type])) {
> +            if (((width % PLT_PIXELS_PER_BYTE[type]) == 0) || (
> +                    (stride - (width / PLT_PIXELS_PER_BYTE[type])) > 1)) {
> +                encoder->usr->error(encoder->usr, "stride overflows (plt)\n");
> +            }
> +        }
> +    } else {
> +        if (stride != width * RGB_BYTES_PER_PIXEL[type]) {
> +            encoder->usr->error(encoder->usr, "stride != width*bytes_per_pixel (rgb)\n");
> +        }
> +    }
> +
> +    // assign the output buffer
> +    if (!encoder_reset(encoder, io_ptr, io_ptr_end)) {
> +        encoder->usr->error(encoder->usr, "lz encoder io reset failed\n");
> +    }
> +
> +    // first read the list of the image segments into the dictionary window
> +    dict_image = glz_dictionary_pre_encode(encoder->id, encoder->usr,
> +                                           encoder->dict, type, width, height, stride,
> +                                           lines, num_lines, usr_context, &win_head_image_dist);
> +    *o_enc_dict_context = (GlzEncDictImageContext *)dict_image;
> +
> +    encoder->cur_image.type = type;
> +    encoder->cur_image.id = dict_image->id;
> +    encoder->cur_image.first_win_seg = dict_image->first_seg;
> +
> +    encode_32(encoder, GUINT32_TO_LE(LZ_MAGIC));
> +    encode_32(encoder, LZ_VERSION);
> +    if (top_down) {
> +        encode(encoder, (type & LZ_IMAGE_TYPE_MASK) | (1 << LZ_IMAGE_TYPE_LOG));
> +    } else {
> +        encode(encoder, (type & LZ_IMAGE_TYPE_MASK));
> +    }
> +
> +    encode_32(encoder, width);
> +    encode_32(encoder, height);
> +    encode_32(encoder, stride);
> +    encode_64(encoder, dict_image->id);
> +    encode_32(encoder, win_head_image_dist);
> +
> +    switch (encoder->cur_image.type) {
> +    case LZ_IMAGE_TYPE_PLT1_BE:
> +    case LZ_IMAGE_TYPE_PLT1_LE:
> +    case LZ_IMAGE_TYPE_PLT4_BE:
> +    case LZ_IMAGE_TYPE_PLT4_LE:
> +    case LZ_IMAGE_TYPE_PLT8:
> +        glz_plt_compress(encoder);
> +        break;
> +    case LZ_IMAGE_TYPE_RGB16:
> +        glz_rgb16_compress(encoder);
> +        break;
> +    case LZ_IMAGE_TYPE_RGB24:
> +        glz_rgb24_compress(encoder);
> +        break;
> +    case LZ_IMAGE_TYPE_RGB32:
> +        glz_rgb32_compress(encoder);
> +        break;
> +    case LZ_IMAGE_TYPE_RGBA:
> +        glz_rgb32_compress(encoder);
> +        glz_rgb_alpha_compress(encoder);
> +        break;
> +    case LZ_IMAGE_TYPE_INVALID:
> +    default:
> +        encoder->usr->error(encoder->usr, "bad image type\n");
> +    }
> +
> +    glz_dictionary_post_encode(encoder->id, encoder->usr, encoder->dict);
> +
> +    // move all the used segments to the free ones
> +    encoder->io.bytes_count -= (encoder->io.end - encoder->io.now);
> +
> +    return encoder->io.bytes_count;
> +}
> diff --git a/server/glz-encoder.h b/server/glz-encoder.h
> new file mode 100644
> index 0000000..93164ed
> --- /dev/null
> +++ b/server/glz-encoder.h
> @@ -0,0 +1,55 @@
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +
> +#ifndef _H_GLZ_ENCODER
> +#define _H_GLZ_ENCODER
> +
> +/* Manging the lz encoding using a dictionary that is shared among encoders */
> +
> +#include <stdint.h>
> +#include "common/lz_common.h"
> +#include "glz-encoder-dict.h"
> +#include "glz_encoder_config.h"
> +
> +typedef void GlzEncoderContext;
> +
> +GlzEncoderContext *glz_encoder_create(uint8_t id, GlzEncDictContext *dictionary,
> +                                      GlzEncoderUsrContext *usr);
> +
> +void glz_encoder_destroy(GlzEncoderContext *opaque_encoder);
> +
> +/*
> +        assumes width is in pixels and stride is in bytes
> +    usr_context       : when an image is released from the window due to capacity overflow,
> +                        usr_context is given as a parameter to the free_image callback.
> +    o_enc_dict_context: if glz_enc_dictionary_remove_image is called, it should be
> +                        called with the o_enc_dict_context that is associated with
> +                        the image.
> +
> +        return: the number of bytes in the compressed data and sets o_enc_dict_context
> +
> +        NOTE  : currently supports only rgb images in which width*bytes_per_pixel = stride OR
> +                palette images in which stride equals the min number of bytes to hold a line.
> +                The stride should be > 0
> +*/
> +int glz_encode(GlzEncoderContext *opaque_encoder, LzImageType type, int width, int height,
> +               int top_down, uint8_t *lines, unsigned int num_lines, int stride,
> +               uint8_t *io_ptr, unsigned int num_io_bytes, GlzUsrImageContext *usr_context,
> +               GlzEncDictImageContext **o_enc_dict_context);
> +
> +
> +#endif // _H_GLZ_ENCODER
> diff --git a/server/glz_encoder.c b/server/glz_encoder.c
> deleted file mode 100644
> index 65f4478..0000000
> --- a/server/glz_encoder.c
> +++ /dev/null
> @@ -1,311 +0,0 @@
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifdef HAVE_CONFIG_H
> -#include <config.h>
> -#endif
> -
> -#include <glib.h>
> -#include <pthread.h>
> -#include <stdio.h>
> -#include "glz_encoder.h"
> -#include "glz_encoder_dictionary_protected.h"
> -
> -
> -/* Holds a specific data for one encoder, and data that is relevant for the current image encoded */
> -typedef struct Encoder {
> -    GlzEncoderUsrContext *usr;
> -    uint8_t id;
> -    SharedDictionary     *dict;
> -
> -    struct {
> -        LzImageType type;
> -        uint32_t id;
> -        uint32_t first_win_seg;
> -    } cur_image;
> -
> -    struct {
> -        uint8_t            *start;
> -        uint8_t            *now;
> -        uint8_t            *end;
> -        size_t bytes_count;
> -        uint8_t            *last_copy;  // pointer to the last byte in which copy count was written
> -    } io;
> -} Encoder;
> -
> -
> -/**************************************************************************
> -* Handling writing the encoded image to the output buffer
> -***************************************************************************/
> -static inline int more_io_bytes(Encoder *encoder)
> -{
> -    uint8_t *io_ptr;
> -    int num_io_bytes = encoder->usr->more_space(encoder->usr, &io_ptr);
> -    encoder->io.bytes_count += num_io_bytes;
> -    encoder->io.now = io_ptr;
> -    encoder->io.end = encoder->io.now + num_io_bytes;
> -    return num_io_bytes;
> -}
> -
> -static inline void encode(Encoder *encoder, uint8_t byte)
> -{
> -    if (encoder->io.now == encoder->io.end) {
> -        if (more_io_bytes(encoder) <= 0) {
> -            encoder->usr->error(encoder->usr, "%s: no more bytes\n", __FUNCTION__);
> -        }
> -        GLZ_ASSERT(encoder->usr, encoder->io.now);
> -    }
> -
> -    GLZ_ASSERT(encoder->usr, encoder->io.now < encoder->io.end);
> -    *(encoder->io.now++) = byte;
> -}
> -
> -static inline void encode_32(Encoder *encoder, unsigned int word)
> -{
> -    encode(encoder, (uint8_t)(word >> 24));
> -    encode(encoder, (uint8_t)(word >> 16) & 0x0000ff);
> -    encode(encoder, (uint8_t)(word >> 8) & 0x0000ff);
> -    encode(encoder, (uint8_t)(word & 0x0000ff));
> -}
> -
> -static inline void encode_64(Encoder *encoder, uint64_t word)
> -{
> -    encode_32(encoder, (uint32_t)(word >> 32));
> -    encode_32(encoder, (uint32_t)(word & 0xffffff));
> -}
> -
> -static inline void encode_copy_count(Encoder *encoder, uint8_t copy_count)
> -{
> -    encode(encoder, copy_count);
> -    encoder->io.last_copy = encoder->io.now - 1; // io_now cannot be the first byte of the buffer
> -}
> -
> -static inline void update_copy_count(Encoder *encoder, uint8_t copy_count)
> -{
> -    GLZ_ASSERT(encoder->usr, encoder->io.last_copy);
> -    *(encoder->io.last_copy) = copy_count;
> -}
> -
> -// decrease the io ptr by 1
> -static inline void compress_output_prev(Encoder *encoder)
> -{
> -    // io_now cannot be the first byte of the buffer
> -    encoder->io.now--;
> -    // the function should be called only when copy count is written unnecessarily by glz_compress
> -    GLZ_ASSERT(encoder->usr, encoder->io.now == encoder->io.last_copy)
> -}
> -
> -static int encoder_reset(Encoder *encoder, uint8_t *io_ptr, uint8_t *io_ptr_end)
> -{
> -    GLZ_ASSERT(encoder->usr, io_ptr <= io_ptr_end);
> -    encoder->io.bytes_count = io_ptr_end - io_ptr;
> -    encoder->io.start = io_ptr;
> -    encoder->io.now = io_ptr;
> -    encoder->io.end = io_ptr_end;
> -    encoder->io.last_copy = NULL;
> -
> -    return TRUE;
> -}
> -
> -/**********************************************************
> -*           Encoding
> -***********************************************************/
> -
> -GlzEncoderContext *glz_encoder_create(uint8_t id, GlzEncDictContext *dictionary,
> -                                      GlzEncoderUsrContext *usr)
> -{
> -    Encoder *encoder;
> -
> -    if (!usr || !usr->error || !usr->warn || !usr->info || !usr->malloc ||
> -        !usr->free || !usr->more_space) {
> -        return NULL;
> -    }
> -
> -    if (!(encoder = (Encoder *)usr->malloc(usr, sizeof(Encoder)))) {
> -        return NULL;
> -    }
> -
> -    encoder->id = id;
> -    encoder->usr = usr;
> -    encoder->dict = (SharedDictionary *)dictionary;
> -
> -    return (GlzEncoderContext *)encoder;
> -}
> -
> -void glz_encoder_destroy(GlzEncoderContext *opaque_encoder)
> -{
> -    Encoder *encoder = (Encoder *)opaque_encoder;
> -
> -    if (!opaque_encoder) {
> -        return;
> -    }
> -
> -    encoder->usr->free(encoder->usr, encoder);
> -}
> -
> -/*
> - * Give hints to the compiler for branch prediction optimization.
> - */
> -#if defined(__GNUC__) && (__GNUC__ > 2)
> -#define LZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
> -#define LZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
> -#else
> -#define LZ_EXPECT_CONDITIONAL(c) (c)
> -#define LZ_UNEXPECT_CONDITIONAL(c) (c)
> -#endif
> -
> -
> -typedef uint8_t BYTE;
> -
> -typedef struct __attribute__ ((__packed__)) one_byte_pixel_t {
> -    BYTE a;
> -} one_byte_pixel_t;
> -
> -typedef struct __attribute__ ((__packed__)) rgb32_pixel_t {
> -    BYTE b;
> -    BYTE g;
> -    BYTE r;
> -    BYTE pad;
> -} rgb32_pixel_t;
> -
> -typedef struct __attribute__ ((__packed__)) rgb24_pixel_t {
> -    BYTE b;
> -    BYTE g;
> -    BYTE r;
> -} rgb24_pixel_t;
> -
> -typedef uint16_t rgb16_pixel_t;
> -
> -#define BOUND_OFFSET 2
> -#define LIMIT_OFFSET 6
> -#define MIN_FILE_SIZE 4
> -
> -#define MAX_PIXEL_SHORT_DISTANCE 4096       // (1 << 12)
> -#define MAX_PIXEL_MEDIUM_DISTANCE 131072    // (1 << 17)  2 ^ (12 + 5)
> -#define MAX_PIXEL_LONG_DISTANCE 33554432    // (1 << 25)  2 ^ (12 + 5 + 8)
> -#define MAX_IMAGE_DIST 16777215             // (1 << 24 - 1)
> -
> -
> -//#define DEBUG_ENCODE
> -
> -
> -#define GLZ_ENCODE_SIZE
> -#include "glz-encode-match.tmpl.c"
> -#define GLZ_ENCODE_MATCH
> -#include "glz-encode-match.tmpl.c"
> -
> -#define LZ_PLT
> -#include "glz-encode.tmpl.c"
> -
> -#define LZ_RGB16
> -#include "glz-encode.tmpl.c"
> -
> -#define LZ_RGB24
> -#include "glz-encode.tmpl.c"
> -
> -#define LZ_RGB32
> -#include "glz-encode.tmpl.c"
> -
> -#define LZ_RGB_ALPHA
> -#include "glz-encode.tmpl.c"
> -
> -
> -int glz_encode(GlzEncoderContext *opaque_encoder,
> -               LzImageType type, int width, int height, int top_down,
> -               uint8_t *lines, unsigned int num_lines, int stride,
> -               uint8_t *io_ptr, unsigned int num_io_bytes,
> -               GlzUsrImageContext *usr_context, GlzEncDictImageContext **o_enc_dict_context)
> -{
> -    Encoder *encoder = (Encoder *)opaque_encoder;
> -    WindowImage *dict_image;
> -    uint8_t *io_ptr_end = io_ptr + num_io_bytes;
> -    uint32_t win_head_image_dist;
> -
> -    if (IS_IMAGE_TYPE_PLT[type]) {
> -        if (stride > (width / PLT_PIXELS_PER_BYTE[type])) {
> -            if (((width % PLT_PIXELS_PER_BYTE[type]) == 0) || (
> -                    (stride - (width / PLT_PIXELS_PER_BYTE[type])) > 1)) {
> -                encoder->usr->error(encoder->usr, "stride overflows (plt)\n");
> -            }
> -        }
> -    } else {
> -        if (stride != width * RGB_BYTES_PER_PIXEL[type]) {
> -            encoder->usr->error(encoder->usr, "stride != width*bytes_per_pixel (rgb)\n");
> -        }
> -    }
> -
> -    // assign the output buffer
> -    if (!encoder_reset(encoder, io_ptr, io_ptr_end)) {
> -        encoder->usr->error(encoder->usr, "lz encoder io reset failed\n");
> -    }
> -
> -    // first read the list of the image segments into the dictionary window
> -    dict_image = glz_dictionary_pre_encode(encoder->id, encoder->usr,
> -                                           encoder->dict, type, width, height, stride,
> -                                           lines, num_lines, usr_context, &win_head_image_dist);
> -    *o_enc_dict_context = (GlzEncDictImageContext *)dict_image;
> -
> -    encoder->cur_image.type = type;
> -    encoder->cur_image.id = dict_image->id;
> -    encoder->cur_image.first_win_seg = dict_image->first_seg;
> -
> -    encode_32(encoder, GUINT32_TO_LE(LZ_MAGIC));
> -    encode_32(encoder, LZ_VERSION);
> -    if (top_down) {
> -        encode(encoder, (type & LZ_IMAGE_TYPE_MASK) | (1 << LZ_IMAGE_TYPE_LOG));
> -    } else {
> -        encode(encoder, (type & LZ_IMAGE_TYPE_MASK));
> -    }
> -
> -    encode_32(encoder, width);
> -    encode_32(encoder, height);
> -    encode_32(encoder, stride);
> -    encode_64(encoder, dict_image->id);
> -    encode_32(encoder, win_head_image_dist);
> -
> -    switch (encoder->cur_image.type) {
> -    case LZ_IMAGE_TYPE_PLT1_BE:
> -    case LZ_IMAGE_TYPE_PLT1_LE:
> -    case LZ_IMAGE_TYPE_PLT4_BE:
> -    case LZ_IMAGE_TYPE_PLT4_LE:
> -    case LZ_IMAGE_TYPE_PLT8:
> -        glz_plt_compress(encoder);
> -        break;
> -    case LZ_IMAGE_TYPE_RGB16:
> -        glz_rgb16_compress(encoder);
> -        break;
> -    case LZ_IMAGE_TYPE_RGB24:
> -        glz_rgb24_compress(encoder);
> -        break;
> -    case LZ_IMAGE_TYPE_RGB32:
> -        glz_rgb32_compress(encoder);
> -        break;
> -    case LZ_IMAGE_TYPE_RGBA:
> -        glz_rgb32_compress(encoder);
> -        glz_rgb_alpha_compress(encoder);
> -        break;
> -    case LZ_IMAGE_TYPE_INVALID:
> -    default:
> -        encoder->usr->error(encoder->usr, "bad image type\n");
> -    }
> -
> -    glz_dictionary_post_encode(encoder->id, encoder->usr, encoder->dict);
> -
> -    // move all the used segments to the free ones
> -    encoder->io.bytes_count -= (encoder->io.end - encoder->io.now);
> -
> -    return encoder->io.bytes_count;
> -}
> diff --git a/server/glz_encoder.h b/server/glz_encoder.h
> deleted file mode 100644
> index e91f515..0000000
> --- a/server/glz_encoder.h
> +++ /dev/null
> @@ -1,55 +0,0 @@
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -
> -#ifndef _H_GLZ_ENCODER
> -#define _H_GLZ_ENCODER
> -
> -/* Manging the lz encoding using a dictionary that is shared among encoders */
> -
> -#include <stdint.h>
> -#include "common/lz_common.h"
> -#include "glz_encoder_dictionary.h"
> -#include "glz_encoder_config.h"
> -
> -typedef void GlzEncoderContext;
> -
> -GlzEncoderContext *glz_encoder_create(uint8_t id, GlzEncDictContext *dictionary,
> -                                      GlzEncoderUsrContext *usr);
> -
> -void glz_encoder_destroy(GlzEncoderContext *opaque_encoder);
> -
> -/*
> -        assumes width is in pixels and stride is in bytes
> -    usr_context       : when an image is released from the window due to capacity overflow,
> -                        usr_context is given as a parameter to the free_image callback.
> -    o_enc_dict_context: if glz_enc_dictionary_remove_image is called, it should be
> -                        called with the o_enc_dict_context that is associated with
> -                        the image.
> -
> -        return: the number of bytes in the compressed data and sets o_enc_dict_context
> -
> -        NOTE  : currently supports only rgb images in which width*bytes_per_pixel = stride OR
> -                palette images in which stride equals the min number of bytes to hold a line.
> -                The stride should be > 0
> -*/
> -int glz_encode(GlzEncoderContext *opaque_encoder, LzImageType type, int width, int height,
> -               int top_down, uint8_t *lines, unsigned int num_lines, int stride,
> -               uint8_t *io_ptr, unsigned int num_io_bytes, GlzUsrImageContext *usr_context,
> -               GlzEncDictImageContext **o_enc_dict_context);
> -
> -
> -#endif // _H_GLZ_ENCODER
> diff --git a/server/glz_encoder_dictionary.c b/server/glz_encoder_dictionary.c
> deleted file mode 100644
> index 70226e1..0000000
> --- a/server/glz_encoder_dictionary.c
> +++ /dev/null
> @@ -1,633 +0,0 @@
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifdef HAVE_CONFIG_H
> -#include <config.h>
> -#endif
> -
> -#include <pthread.h>
> -#include <string.h>
> -#include <stdio.h>
> -
> -#include "glz_encoder_dictionary.h"
> -#include "glz_encoder_dictionary_protected.h"
> -
> -/* turning all used images to free ones. If they are alive, calling the free_image callback for
> -   each one */
> -static inline void __glz_dictionary_window_reset_images(SharedDictionary *dict)
> -{
> -    WindowImage *tmp;
> -
> -    while (dict->window.used_images_head) {
> -        tmp = dict->window.used_images_head;
> -        dict->window.used_images_head = dict->window.used_images_head->next;
> -        if (tmp->is_alive) {
> -            dict->cur_usr->free_image(dict->cur_usr, tmp->usr_context);
> -        }
> -        tmp->next = dict->window.free_images;
> -        tmp->is_alive = FALSE;
> -        dict->window.free_images = tmp;
> -    }
> -    dict->window.used_images_tail = NULL;
> -}
> -
> -/* allocate window fields (no reset)*/
> -static int glz_dictionary_window_create(SharedDictionary *dict, uint32_t size)
> -{
> -    if (size > LZ_MAX_WINDOW_SIZE) {
> -        return FALSE;
> -    }
> -
> -    dict->window.size_limit = size;
> -    dict->window.segs = (WindowImageSegment *)(
> -            dict->cur_usr->malloc(dict->cur_usr, sizeof(WindowImageSegment) * INIT_IMAGE_SEGS_NUM));
> -
> -    if (!dict->window.segs) {
> -        return FALSE;
> -    }
> -
> -    dict->window.segs_quota = INIT_IMAGE_SEGS_NUM;
> -
> -    dict->window.encoders_heads = (uint32_t *)dict->cur_usr->malloc(dict->cur_usr,
> -                                                            sizeof(uint32_t) * dict->max_encoders);
> -
> -    if (!dict->window.encoders_heads) {
> -        dict->cur_usr->free(dict->cur_usr, dict->window.segs);
> -        return FALSE;
> -    }
> -
> -    dict->window.used_images_head = NULL;
> -    dict->window.used_images_tail = NULL;
> -    dict->window.free_images = NULL;
> -    dict->window.pixels_so_far = 0;
> -
> -    return TRUE;
> -}
> -
> -/* initializes an empty window (segs and encoder_heads should be pre allocated.
> -   resets the image infos, and calls the free_image usr callback*/
> -static void glz_dictionary_window_reset(SharedDictionary *dict)
> -{
> -    uint32_t i;
> -    WindowImageSegment *seg, *last_seg;
> -
> -    last_seg = dict->window.segs + dict->window.segs_quota;
> -    /* reset free segs list */
> -    dict->window.free_segs_head = 0;
> -    for (seg = dict->window.segs, i = 0; seg < last_seg; seg++, i++) {
> -        seg->next = i + 1;
> -        seg->image = NULL;
> -        seg->lines = NULL;
> -        seg->lines_end = NULL;
> -        seg->pixels_num = 0;
> -        seg->pixels_so_far = 0;
> -    }
> -    dict->window.segs[dict->window.segs_quota - 1].next = NULL_IMAGE_SEG_ID;
> -
> -    dict->window.used_segs_head = NULL_IMAGE_SEG_ID;
> -    dict->window.used_segs_tail = NULL_IMAGE_SEG_ID;
> -
> -    // reset encoders heads
> -    for (i = 0; i < dict->max_encoders; i++) {
> -        dict->window.encoders_heads[i] = NULL_IMAGE_SEG_ID;
> -    }
> -
> -    __glz_dictionary_window_reset_images(dict);
> -}
> -
> -static inline void glz_dictionary_reset_hash(SharedDictionary *dict)
> -{
> -    memset(dict->htab, 0, sizeof(HashEntry) * HASH_SIZE * HASH_CHAIN_SIZE);
> -#ifdef CHAINED_HASH
> -    memset(dict->htab_counter, 0, HASH_SIZE * sizeof(uint8_t));
> -#endif
> -}
> -
> -static inline void glz_dictionary_window_destroy(SharedDictionary *dict)
> -{
> -    __glz_dictionary_window_reset_images(dict);
> -
> -    if (dict->window.segs) {
> -        dict->cur_usr->free(dict->cur_usr, dict->window.segs);
> -        dict->window.segs = NULL;
> -    }
> -
> -    while (dict->window.free_images) {
> -        WindowImage *tmp = dict->window.free_images;
> -        dict->window.free_images = tmp->next;
> -
> -        dict->cur_usr->free(dict->cur_usr, tmp);
> -    }
> -
> -    if (dict->window.encoders_heads) {
> -        dict->cur_usr->free(dict->cur_usr, dict->window.encoders_heads);
> -        dict->window.encoders_heads = NULL;
> -    }
> -}
> -
> -/* logic removal only */
> -static inline void glz_dictionary_window_kill_image(SharedDictionary *dict, WindowImage *image)
> -{
> -    image->is_alive = FALSE;
> -}
> -
> -GlzEncDictContext *glz_enc_dictionary_create(uint32_t size, uint32_t max_encoders,
> -                                             GlzEncoderUsrContext *usr)
> -{
> -    SharedDictionary *dict;
> -
> -    if (!(dict = (SharedDictionary *)usr->malloc(usr,
> -                                                 sizeof(SharedDictionary)))) {
> -        return NULL;
> -    }
> -
> -    dict->cur_usr = usr;
> -    dict->last_image_id = 0;
> -    dict->max_encoders = max_encoders;
> -
> -    pthread_mutex_init(&dict->lock, NULL);
> -    pthread_rwlock_init(&dict->rw_alloc_lock, NULL);
> -
> -    dict->window.encoders_heads = NULL;
> -
> -    // alloc window fields and reset
> -    if (!glz_dictionary_window_create(dict, size)) {
> -        dict->cur_usr->free(usr, dict);
> -        return NULL;
> -    }
> -
> -    // reset window and hash
> -    glz_enc_dictionary_reset((GlzEncDictContext *)dict, usr);
> -
> -    return (GlzEncDictContext *)dict;
> -}
> -
> -void glz_enc_dictionary_get_restore_data(GlzEncDictContext *opaque_dict,
> -                                         GlzEncDictRestoreData *out_data, GlzEncoderUsrContext *usr)
> -{
> -    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
> -    dict->cur_usr = usr;
> -    GLZ_ASSERT(dict->cur_usr, opaque_dict);
> -    GLZ_ASSERT(dict->cur_usr, out_data);
> -
> -    out_data->last_image_id = dict->last_image_id;
> -    out_data->max_encoders = dict->max_encoders;
> -    out_data->size = dict->window.size_limit;
> -}
> -
> -GlzEncDictContext *glz_enc_dictionary_restore(GlzEncDictRestoreData *restore_data,
> -                                              GlzEncoderUsrContext *usr)
> -{
> -    if (!restore_data) {
> -        return NULL;
> -    }
> -    SharedDictionary *ret = (SharedDictionary *)glz_enc_dictionary_create(
> -            restore_data->size, restore_data->max_encoders, usr);
> -    ret->last_image_id = restore_data->last_image_id;
> -    return ((GlzEncDictContext *)ret);
> -}
> -
> -void glz_enc_dictionary_reset(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr)
> -{
> -    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
> -    dict->cur_usr = usr;
> -    GLZ_ASSERT(dict->cur_usr, opaque_dict);
> -
> -    dict->last_image_id = 0;
> -    glz_dictionary_window_reset(dict);
> -    glz_dictionary_reset_hash(dict);
> -}
> -
> -void glz_enc_dictionary_destroy(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr)
> -{
> -    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
> -
> -    if (!opaque_dict) {
> -        return;
> -    }
> -
> -    dict->cur_usr = usr;
> -    glz_dictionary_window_destroy(dict);
> -
> -    pthread_mutex_destroy(&dict->lock);
> -    pthread_rwlock_destroy(&dict->rw_alloc_lock);
> -
> -    dict->cur_usr->free(dict->cur_usr, dict);
> -}
> -
> -uint32_t glz_enc_dictionary_get_size(GlzEncDictContext *opaque_dict)
> -{
> -    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
> -
> -    if (!opaque_dict) {
> -        return 0;
> -    }
> -    return dict->window.size_limit;
> -}
> -
> -/* doesn't call the remove image callback */
> -void glz_enc_dictionary_remove_image(GlzEncDictContext *opaque_dict,
> -                                     GlzEncDictImageContext *opaque_image,
> -                                     GlzEncoderUsrContext *usr)
> -{
> -    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
> -    WindowImage *image = (WindowImage *)opaque_image;
> -    dict->cur_usr = usr;
> -    GLZ_ASSERT(dict->cur_usr, opaque_image && opaque_dict);
> -
> -    glz_dictionary_window_kill_image(dict, image);
> -}
> -
> -/***********************************************************************************
> - Mutators of the window. Should be called by the encoder before and after encoding.
> - ***********************************************************************************/
> -
> -static inline int __get_pixels_num(LzImageType image_type, unsigned int num_lines, int stride)
> -{
> -    if (IS_IMAGE_TYPE_RGB[image_type]) {
> -        return num_lines * stride / RGB_BYTES_PER_PIXEL[image_type];
> -    } else {
> -        return num_lines * stride * PLT_PIXELS_PER_BYTE[image_type];
> -    }
> -}
> -
> -static void __glz_dictionary_window_segs_realloc(SharedDictionary *dict)
> -{
> -    WindowImageSegment *new_segs;
> -    uint32_t new_quota = (MAX_IMAGE_SEGS_NUM < (dict->window.segs_quota * 2)) ?
> -        MAX_IMAGE_SEGS_NUM : (dict->window.segs_quota * 2);
> -    WindowImageSegment *seg;
> -    uint32_t i;
> -
> -    pthread_rwlock_wrlock(&dict->rw_alloc_lock);
> -
> -    if (dict->window.segs_quota == MAX_IMAGE_SEGS_NUM) {
> -        dict->cur_usr->error(dict->cur_usr, "overflow in image segments window\n");
> -    }
> -
> -    new_segs = (WindowImageSegment*)dict->cur_usr->malloc(
> -            dict->cur_usr, sizeof(WindowImageSegment) * new_quota);
> -
> -    if (!new_segs) {
> -        dict->cur_usr->error(dict->cur_usr,
> -                             "realloc of dictionary window failed\n");
> -    }
> -
> -    memcpy(new_segs, dict->window.segs,
> -           sizeof(WindowImageSegment) * dict->window.segs_quota);
> -
> -    // resetting the new elements
> -    for (i = dict->window.segs_quota, seg = new_segs + i; i < new_quota; i++, seg++) {
> -        seg->image = NULL;
> -        seg->lines = NULL;
> -        seg->lines_end = NULL;
> -        seg->pixels_num = 0;
> -        seg->pixels_so_far = 0;
> -        seg->next = i + 1;
> -    }
> -    new_segs[new_quota - 1].next = dict->window.free_segs_head;
> -    dict->window.free_segs_head = dict->window.segs_quota;
> -
> -    dict->cur_usr->free(dict->cur_usr, dict->window.segs);
> -    dict->window.segs = new_segs;
> -    dict->window.segs_quota = new_quota;
> -
> -    pthread_rwlock_unlock(&dict->rw_alloc_lock);
> -}
> -
> -/* NOTE - it also updates the used_images_list*/
> -static WindowImage *__glz_dictionary_window_alloc_image(SharedDictionary *dict)
> -{
> -    WindowImage *ret;
> -
> -    if (dict->window.free_images) {
> -        ret = dict->window.free_images;
> -        dict->window.free_images = ret->next;
> -    } else {
> -        if (!(ret = (WindowImage *)dict->cur_usr->malloc(dict->cur_usr,
> -                                                         sizeof(*ret)))) {
> -            return NULL;
> -        }
> -    }
> -
> -    ret->next = NULL;
> -    if (dict->window.used_images_tail) {
> -        dict->window.used_images_tail->next = ret;
> -    }
> -    dict->window.used_images_tail = ret;
> -
> -    if (!dict->window.used_images_head) {
> -        dict->window.used_images_head = ret;
> -    }
> -    return ret;
> -}
> -
> -/* NOTE - it doesn't update the used_segs list*/
> -static uint32_t __glz_dictionary_window_alloc_image_seg(SharedDictionary *dict)
> -{
> -    uint32_t seg_id;
> -    WindowImageSegment *seg;
> -
> -    // TODO: when is it best to realloc? when full or when half full?
> -    if (dict->window.free_segs_head == NULL_IMAGE_SEG_ID) {
> -        __glz_dictionary_window_segs_realloc(dict);
> -    }
> -
> -    GLZ_ASSERT(dict->cur_usr, dict->window.free_segs_head != NULL_IMAGE_SEG_ID);
> -
> -    seg_id = dict->window.free_segs_head;
> -    seg = dict->window.segs + seg_id;
> -    dict->window.free_segs_head = seg->next;
> -
> -    return seg_id;
> -}
> -
> -/* moves image to free list and "kill" it. Calls the free_image callback if was alive. */
> -static inline void __glz_dictionary_window_free_image(SharedDictionary *dict, WindowImage *image)
> -{
> -    if (image->is_alive) {
> -        dict->cur_usr->free_image(dict->cur_usr, image->usr_context);
> -    }
> -    image->is_alive = FALSE;
> -    image->next = dict->window.free_images;
> -    dict->window.free_images = image;
> -}
> -
> -/* moves all the segments that were associated with the images to the free segments */
> -static inline void __glz_dictionary_window_free_image_segs(SharedDictionary *dict,
> -                                                           WindowImage *image)
> -{
> -    uint32_t old_free_head = dict->window.free_segs_head;
> -    uint32_t seg_id, next_seg_id;
> -
> -    GLZ_ASSERT(dict->cur_usr, image->first_seg != NULL_IMAGE_SEG_ID);
> -    dict->window.free_segs_head = image->first_seg;
> -
> -    // retrieving the last segment of the image
> -    for (seg_id = image->first_seg, next_seg_id = dict->window.segs[seg_id].next;
> -         (next_seg_id != NULL_IMAGE_SEG_ID) && (dict->window.segs[next_seg_id].image == image);
> -         seg_id = next_seg_id, next_seg_id = dict->window.segs[seg_id].next) {
> -    }
> -
> -    // concatenate the free list
> -    dict->window.segs[seg_id].next = old_free_head;
> -}
> -
> -/* Returns the logical head of the window after we add an image with the give size to its tail.
> -   Returns NULL when the window is empty, of when we have to empty the window in order
> -   to insert the new image. */
> -static WindowImage *glz_dictionary_window_get_new_head(SharedDictionary *dict, int new_image_size)
> -{
> -    uint32_t cur_win_size;
> -    WindowImage *cur_head;
> -
> -    if ((uint32_t)new_image_size > dict->window.size_limit) {
> -        dict->cur_usr->error(dict->cur_usr, "image is bigger than window\n");
> -    }
> -
> -    GLZ_ASSERT(dict->cur_usr, new_image_size < dict->window.size_limit)
> -
> -    // the window is empty
> -    if (!dict->window.used_images_head) {
> -        return NULL;
> -    }
> -
> -    GLZ_ASSERT(dict->cur_usr, dict->window.used_segs_head != NULL_IMAGE_SEG_ID);
> -    GLZ_ASSERT(dict->cur_usr, dict->window.used_segs_tail != NULL_IMAGE_SEG_ID);
> -
> -    // used_segs_head is the latest logical head (the physical head may preceed it)
> -    cur_head = dict->window.segs[dict->window.used_segs_head].image;
> -    cur_win_size = dict->window.segs[dict->window.used_segs_tail].pixels_num +
> -        dict->window.segs[dict->window.used_segs_tail].pixels_so_far -
> -        dict->window.segs[dict->window.used_segs_head].pixels_so_far;
> -
> -    while ((cur_win_size + new_image_size) > dict->window.size_limit) {
> -        GLZ_ASSERT(dict->cur_usr, cur_head);
> -        cur_win_size -= cur_head->size;
> -        cur_head = cur_head->next;
> -    }
> -
> -    return cur_head;
> -}
> -
> -static inline int glz_dictionary_is_in_use(SharedDictionary *dict)
> -{
> -    uint32_t i = 0;
> -    for (i = 0; i < dict->max_encoders; i++) {
> -        if (dict->window.encoders_heads[i] != NULL_IMAGE_SEG_ID) {
> -            return TRUE;
> -        }
> -    }
> -    return FALSE;
> -}
> -
> -/* remove from the window (and free relevant data) the images between the oldest physical head
> -   (inclusive) and the end_image (exclusive). If end_image is NULL, empties the window*/
> -static void glz_dictionary_window_remove_head(SharedDictionary *dict, uint32_t encoder_id,
> -                                              WindowImage *end_image)
> -{
> -    // note that the segs list heads (one per encoder) may be different than the
> -    // used_segs_head and it is updated somewhere else
> -    while (dict->window.used_images_head != end_image) {
> -        WindowImage *image = dict->window.used_images_head;
> -
> -        __glz_dictionary_window_free_image_segs(dict, image);
> -        dict->window.used_images_head = image->next;
> -        __glz_dictionary_window_free_image(dict, image);
> -    }
> -
> -    if (!dict->window.used_images_head) {
> -        dict->window.used_segs_head = NULL_IMAGE_SEG_ID;
> -        dict->window.used_segs_tail = NULL_IMAGE_SEG_ID;
> -        dict->window.used_images_tail = NULL;
> -    } else {
> -        dict->window.used_segs_head = end_image->first_seg;
> -    }
> -}
> -
> -static uint32_t glz_dictionary_window_alloc_image_seg(SharedDictionary *dict, WindowImage* image,
> -                                                      int size, int stride,
> -                                                      uint8_t *lines, unsigned int num_lines)
> -{
> -    uint32_t seg_id = __glz_dictionary_window_alloc_image_seg(dict);
> -    WindowImageSegment *seg = &dict->window.segs[seg_id];
> -
> -    seg->image = image;
> -    seg->lines = lines;
> -    seg->lines_end = lines + num_lines * stride;
> -    seg->pixels_num = size;
> -    seg->pixels_so_far = dict->window.pixels_so_far;
> -    dict->window.pixels_so_far += seg->pixels_num;
> -
> -    seg->next = NULL_IMAGE_SEG_ID;
> -
> -    return seg_id;
> -}
> -
> -static WindowImage *glz_dictionary_window_add_image(SharedDictionary *dict, LzImageType image_type,
> -                                                    int image_size, int image_height,
> -                                                    int image_stride, uint8_t *first_lines,
> -                                                    unsigned int num_first_lines,
> -                                                    GlzUsrImageContext *usr_image_context)
> -{
> -    unsigned int num_lines = num_first_lines;
> -    unsigned int row;
> -    uint32_t seg_id, prev_seg_id;
> -    uint8_t* lines = first_lines;
> -    // alloc image info,update used head tail,  if used_head null - update  head
> -    WindowImage *image = __glz_dictionary_window_alloc_image(dict);
> -    image->id = dict->last_image_id++;
> -    image->size = image_size;
> -    image->type = image_type;
> -    image->usr_context = usr_image_context;
> -
> -    if (num_lines <= 0) {
> -        num_lines = dict->cur_usr->more_lines(dict->cur_usr, &lines);
> -        if (num_lines <= 0) {
> -            dict->cur_usr->error(dict->cur_usr, "more lines failed\n");
> -        }
> -    }
> -
> -    for (row = 0;;) {
> -        seg_id = glz_dictionary_window_alloc_image_seg(dict, image,
> -                                                       image_size * num_lines / image_height,
> -                                                       image_stride,
> -                                                       lines, num_lines);
> -        if (row == 0) {
> -            image->first_seg = seg_id;
> -        } else {
> -            dict->window.segs[prev_seg_id].next = seg_id;
> -        }
> -
> -        row += num_lines;
> -        if (row < (uint32_t)image_height) {
> -            num_lines = dict->cur_usr->more_lines(dict->cur_usr, &lines);
> -            if (num_lines <= 0) {
> -                dict->cur_usr->error(dict->cur_usr, "more lines failed\n");
> -            }
> -        } else {
> -            break;
> -        }
> -        prev_seg_id = seg_id;
> -    }
> -
> -    if (dict->window.used_segs_tail == NULL_IMAGE_SEG_ID) {
> -        dict->window.used_segs_head = image->first_seg;
> -        dict->window.used_segs_tail = seg_id;
> -    } else {
> -        int prev_tail = dict->window.used_segs_tail;
> -
> -        // The used segs may be in use by another thread which is during encoding
> -        // (read-only use - when going over the segs of an image,
> -        // see glz_encode_tmpl::compress).
> -        // Thus, the 'next' field of the list's tail can be accessed only
> -        // after all the new tail's data was set. Note that we are relying on
> -        // an atomic assignment (32 bit variable).
> -        // For the other thread that may read 'next' of the old tail, NULL_IMAGE_SEG_ID
> -        // is equivalent to a segment with an image id that is different
> -        // from the image id of the tail, so we don't need to further protect this field.
> -        dict->window.segs[prev_tail].next = image->first_seg;
> -        dict->window.used_segs_tail = seg_id;
> -    }
> -    image->is_alive = TRUE;
> -
> -    return image;
> -}
> -
> -WindowImage *glz_dictionary_pre_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
> -                                       SharedDictionary *dict, LzImageType image_type,
> -                                       int image_width, int image_height, int image_stride,
> -                                       uint8_t *first_lines, unsigned int num_first_lines,
> -                                       GlzUsrImageContext *usr_image_context,
> -                                       uint32_t *image_head_dist)
> -{
> -    WindowImage *new_win_head, *ret;
> -    int image_size;
> -
> -
> -    pthread_mutex_lock(&dict->lock);
> -
> -    dict->cur_usr = usr;
> -    GLZ_ASSERT(dict->cur_usr, dict->window.encoders_heads[encoder_id] == NULL_IMAGE_SEG_ID);
> -
> -    image_size = __get_pixels_num(image_type, image_height, image_stride);
> -    new_win_head = glz_dictionary_window_get_new_head(dict, image_size);
> -
> -    if (!glz_dictionary_is_in_use(dict)) {
> -        glz_dictionary_window_remove_head(dict, encoder_id, new_win_head);
> -    }
> -
> -    ret = glz_dictionary_window_add_image(dict, image_type, image_size, image_height, image_stride,
> -                                          first_lines, num_first_lines, usr_image_context);
> -
> -    if (new_win_head) {
> -        dict->window.encoders_heads[encoder_id] = new_win_head->first_seg;
> -        *image_head_dist = (uint32_t)(ret->id - new_win_head->id); // shouldn't be greater than 32
> -                                                                   // bit because the window size is
> -                                                                   // limited to 2^25
> -    } else {
> -        dict->window.encoders_heads[encoder_id] = ret->first_seg;
> -        *image_head_dist = 0;
> -    }
> -
> -
> -    // update encoders head  (the other heads were already updated)
> -    pthread_mutex_unlock(&dict->lock);
> -    pthread_rwlock_rdlock(&dict->rw_alloc_lock);
> -    return ret;
> -}
> -
> -void glz_dictionary_post_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
> -                                SharedDictionary *dict)
> -{
> -    uint32_t i;
> -    uint32_t early_head_seg = NULL_IMAGE_SEG_ID;
> -    uint32_t this_encoder_head_seg;
> -
> -    pthread_rwlock_unlock(&dict->rw_alloc_lock);
> -    pthread_mutex_lock(&dict->lock);
> -    dict->cur_usr = usr;
> -
> -    GLZ_ASSERT(dict->cur_usr, dict->window.encoders_heads[encoder_id] != NULL_IMAGE_SEG_ID);
> -    // get the earliest head in use (not including this encoder head)
> -    for (i = 0; i < dict->max_encoders; i++) {
> -        if (i != encoder_id) {
> -            if (IMAGE_SEG_IS_EARLIER(dict, dict->window.encoders_heads[i], early_head_seg)) {
> -                early_head_seg = dict->window.encoders_heads[i];
> -            }
> -        }
> -    }
> -
> -    // possible only if early_head_seg == NULL
> -    if (IMAGE_SEG_IS_EARLIER(dict, dict->window.used_segs_head, early_head_seg)) {
> -        early_head_seg = dict->window.used_segs_head;
> -    }
> -
> -    this_encoder_head_seg = dict->window.encoders_heads[encoder_id];
> -
> -    GLZ_ASSERT(dict->cur_usr, early_head_seg != NULL_IMAGE_SEG_ID);
> -
> -    if (IMAGE_SEG_IS_EARLIER(dict, this_encoder_head_seg, early_head_seg)) {
> -        GLZ_ASSERT(dict->cur_usr,
> -                   this_encoder_head_seg == dict->window.used_images_head->first_seg);
> -        glz_dictionary_window_remove_head(dict, encoder_id,
> -                                          dict->window.segs[early_head_seg].image);
> -    }
> -
> -
> -    dict->window.encoders_heads[encoder_id] = NULL_IMAGE_SEG_ID;
> -    pthread_mutex_unlock(&dict->lock);
> -}
> diff --git a/server/glz_encoder_dictionary.h b/server/glz_encoder_dictionary.h
> deleted file mode 100644
> index eb57aa5..0000000
> --- a/server/glz_encoder_dictionary.h
> +++ /dev/null
> @@ -1,69 +0,0 @@
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -
> -#ifndef _H_GLZ_ENCODER_DICTIONARY
> -#define _H_GLZ_ENCODER_DICTIONARY
> -
> -#include <stdint.h>
> -#include "glz_encoder_config.h"
> -
> -/*
> -    Interface for maintaining lz dictionary that is shared among several encoders.
> -    The interface for accessing the dictionary for encoding purposes is located in
> -    glz_encoder_dictionary_protected.h
> -*/
> -
> -typedef void GlzEncDictContext;
> -typedef void GlzEncDictImageContext;
> -
> -/* NOTE: DISPLAY_MIGRATE_DATA_VERSION should change in case GlzEncDictRestoreData changes*/
> -typedef struct GlzEncDictRestoreData {
> -    uint32_t size;
> -    uint32_t max_encoders;
> -    uint64_t last_image_id;
> -} GlzEncDictRestoreData;
> -
> -/* size        : maximal number of pixels occupying the window
> -   max_encoders: maximal number of encoders that use the dictionary
> -   usr         : callbacks */
> -GlzEncDictContext *glz_enc_dictionary_create(uint32_t size, uint32_t max_encoders,
> -                                             GlzEncoderUsrContext *usr);
> -
> -void glz_enc_dictionary_destroy(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr);
> -
> -/* returns the window capacity in pixels */
> -uint32_t glz_enc_dictionary_get_size(GlzEncDictContext *);
> -
> -/* returns the current state of the dictionary.
> -   NOTE - you should use it only when no encoder uses the dictionary. */
> -void glz_enc_dictionary_get_restore_data(GlzEncDictContext *opaque_dict,
> -                                         GlzEncDictRestoreData *out_data,
> -                                         GlzEncoderUsrContext *usr);
> -
> -/* creates a dictionary and initialized it by use the given info */
> -GlzEncDictContext *glz_enc_dictionary_restore(GlzEncDictRestoreData *restore_data,
> -                                              GlzEncoderUsrContext *usr);
> -
> -/*  NOTE - you should use this routine only when no encoder uses the dictionary. */
> -void glz_enc_dictionary_reset(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr);
> -
> -/* image: the context returned by the encoder when the image was encoded.
> -   NOTE - you should use this routine only when no encoder uses the dictionary.*/
> -void glz_enc_dictionary_remove_image(GlzEncDictContext *opaque_dict,
> -                                     GlzEncDictImageContext *image, GlzEncoderUsrContext *usr);
> -
> -#endif // _H_GLZ_ENCODER_DICTIONARY
> diff --git a/server/glz_encoder_dictionary_protected.h b/server/glz_encoder_dictionary_protected.h
> deleted file mode 100644
> index 098684f..0000000
> --- a/server/glz_encoder_dictionary_protected.h
> +++ /dev/null
> @@ -1,186 +0,0 @@
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -
> -#ifndef _H_GLZ_ENCODER_DICTIONARY_PROTECTED
> -#define _H_GLZ_ENCODER_DICTIONARY_PROTECTED
> -
> -/* Interface for using the dictionary for encoding.
> -   Data structures are exposed for the encoder for efficiency
> -   purposes. */
> -typedef struct WindowImage WindowImage;
> -typedef struct WindowImageSegment WindowImageSegment;
> -
> -
> -//#define CHAINED_HASH
> -
> -#ifdef CHAINED_HASH
> -#define HASH_SIZE_LOG 16
> -#define HASH_CHAIN_SIZE 4
> -#else
> -#define HASH_SIZE_LOG 20
> -#define HASH_CHAIN_SIZE 1
> -#endif
> -
> -#define HASH_SIZE (1 << HASH_SIZE_LOG)
> -#define HASH_MASK (HASH_SIZE - 1)
> -
> -typedef struct HashEntry HashEntry;
> -
> -typedef struct SharedDictionary SharedDictionary;
> -
> -struct WindowImage {
> -    uint64_t id;
> -    LzImageType type;
> -    int size;                    // in pixels
> -    uint32_t first_seg;
> -    GlzUsrImageContext  *usr_context;
> -    WindowImage*       next;
> -    uint8_t is_alive;
> -};
> -
> -#define MAX_IMAGE_SEGS_NUM (0xffffffff)
> -#define NULL_IMAGE_SEG_ID MAX_IMAGE_SEGS_NUM
> -#define INIT_IMAGE_SEGS_NUM 1000
> -
> -/* Images can be separated into several chunks. The basic unit of the
> -   dictionary window is one image segment. Each segment is encoded separately.
> -   An encoded match can refer to only one segment.*/
> -struct WindowImageSegment {
> -    WindowImage     *image;
> -    void            *lines;
> -    void            *lines_end;
> -    uint32_t pixels_num;            // Number of pixels in the segment
> -    uint64_t pixels_so_far;         // Total no. pixels passed through the window till this segment.
> -                                    // NOTE - never use size delta independently. It should
> -                                    // always be used with respect to a previous size delta
> -    uint32_t next;
> -};
> -
> -
> -struct  __attribute__ ((__packed__)) HashEntry {
> -    uint32_t image_seg_idx;
> -    uint32_t ref_pix_idx;
> -};
> -
> -
> -struct SharedDictionary {
> -    struct {
> -        /* The segments storage. A dynamic array.
> -           By referring to a segment by its index, instead of address,
> -           we save space in the hash entries (32bit instead of 64bit) */
> -        WindowImageSegment  *segs;
> -        uint32_t segs_quota;
> -
> -        /* The window is manged as a linked list rather than as a cyclic
> -           array in order to keep the indices of the segments consistent
> -           after reallocation */
> -
> -        /* the window in a resolution of image segments */
> -        uint32_t used_segs_head;             // the latest head
> -        uint32_t used_segs_tail;
> -        uint32_t free_segs_head;
> -
> -        uint32_t            *encoders_heads; // Holds for each encoder (by id), the window head when
> -                                             // it started the encoding.
> -                                             // The head is NULL_IMAGE_SEG_ID when the encoder is
> -                                             // not encoding.
> -
> -        /* the window in a resolution of images. But here the head contains the oldest head*/
> -        WindowImage*        used_images_tail;
> -        WindowImage*        used_images_head;
> -        WindowImage*        free_images;
> -
> -        uint64_t pixels_so_far;
> -        uint32_t size_limit;                 // max number of pixels in a window (per encoder)
> -    } window;
> -
> -    /* Concurrency issues: the reading/writing of each entry field should be atomic.
> -       It is allowed that the reading/writing of the whole entry won't be atomic,
> -       since before we access a reference we check its validity*/
> -#ifdef CHAINED_HASH
> -    HashEntry htab[HASH_SIZE][HASH_CHAIN_SIZE];
> -    uint8_t htab_counter[HASH_SIZE];  //cyclic counter for the next entry in a chain to be assigned
> -#else
> -    HashEntry htab[HASH_SIZE];
> -#endif
> -
> -    uint64_t last_image_id;
> -    uint32_t max_encoders;
> -    pthread_mutex_t lock;
> -    pthread_rwlock_t rw_alloc_lock;
> -    GlzEncoderUsrContext       *cur_usr; // each encoder has other context.
> -};
> -
> -/*
> -    Add the image to the tail of the window.
> -    If possible, release images from the head of the window.
> -    Also perform concurrency related operations.
> -
> -    usr_image_context: when an image is released from the window due to capacity overflow,
> -                       usr_image_context is given as a parameter to the free_image callback.
> -
> -    image_head_dist  : the number of images between the current image and the head of the
> -                       window that is associated with the encoder.
> -*/
> -WindowImage *glz_dictionary_pre_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
> -                                       SharedDictionary *dict, LzImageType image_type,
> -                                       int image_width, int image_height, int image_stride,
> -                                       uint8_t *first_lines, unsigned int num_first_lines,
> -                                       GlzUsrImageContext *usr_image_context,
> -                                       uint32_t *image_head_dist);
> -
> -/*
> -   Performs concurrency related operations.
> -   If possible, release images from the head of the window.
> -*/
> -void glz_dictionary_post_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
> -                                SharedDictionary *dict);
> -
> -#define IMAGE_SEG_IS_EARLIER(dict, dst_seg, src_seg) (                     \
> -    ((src_seg) == NULL_IMAGE_SEG_ID) || (((dst_seg) != NULL_IMAGE_SEG_ID)  \
> -    && ((dict)->window.segs[(dst_seg)].pixels_so_far <                     \
> -       (dict)->window.segs[(src_seg)].pixels_so_far)))
> -
> -
> -#ifdef CHAINED_HASH
> -#define UPDATE_HASH(dict, hval, seg, pix) {                \
> -    uint8_t tmp_count = (dict)->htab_counter[hval];        \
> -    (dict)->htab[hval][tmp_count].image_seg_idx = seg;     \
> -    (dict)->htab[hval][tmp_count].ref_pix_idx = pix;       \
> -    tmp_count = ((tmp_count) + 1) & (HASH_CHAIN_SIZE - 1); \
> -    dict->htab_counter[hval] = tmp_count;                  \
> -}
> -#else
> -#define UPDATE_HASH(dict, hval, seg, pix) { \
> -    (dict)->htab[hval].image_seg_idx = seg; \
> -    (dict)->htab[hval].ref_pix_idx = pix;   \
> -}
> -#endif
> -
> -/* checks if the reference segment is located in the range of the window
> -   of the current encoder */
> -#define REF_SEG_IS_VALID(dict, enc_id, ref_seg, src_seg) ( \
> -    ((ref_seg) == (src_seg)) ||                            \
> -    ((ref_seg)->image &&                                   \
> -     (ref_seg)->image->is_alive &&                         \
> -     (src_seg->image->type == ref_seg->image->type) &&     \
> -     (ref_seg->pixels_so_far <= src_seg->pixels_so_far) && \
> -     ((dict)->window.segs[                                 \
> -        (dict)->window.encoders_heads[enc_id]].pixels_so_far <= \
> -        ref_seg->pixels_so_far)))
> -
> -#endif // _H_GLZ_ENCODER_DICTIONARY_PROTECTED
> diff --git a/server/image-cache.c b/server/image-cache.c
> new file mode 100644
> index 0000000..f4d2ee9
> --- /dev/null
> +++ b/server/image-cache.c
> @@ -0,0 +1,214 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +#include "image-cache.h"
> +#include "red_parse_qxl.h"
> +#include "display-channel.h"
> +
> +static ImageCacheItem *image_cache_find(ImageCache *cache, uint64_t id)
> +{
> +    ImageCacheItem *item = cache->hash_table[id % IMAGE_CACHE_HASH_SIZE];
> +
> +    while (item) {
> +        if (item->id == id) {
> +            return item;
> +        }
> +        item = item->next;
> +    }
> +    return NULL;
> +}
> +
> +int image_cache_hit(ImageCache *cache, uint64_t id)
> +{
> +    ImageCacheItem *item;
> +    if (!(item = image_cache_find(cache, id))) {
> +        return FALSE;
> +    }
> +#ifdef IMAGE_CACHE_AGE
> +    item->age = cache->age;
> +#endif
> +    ring_remove(&item->lru_link);
> +    ring_add(&cache->lru, &item->lru_link);
> +    return TRUE;
> +}
> +
> +static void image_cache_remove(ImageCache *cache, ImageCacheItem *item)
> +{
> +    ImageCacheItem **now;
> +
> +    now = &cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
> +    for (;;) {
> +        spice_assert(*now);
> +        if (*now == item) {
> +            *now = item->next;
> +            break;
> +        }
> +        now = &(*now)->next;
> +    }
> +    ring_remove(&item->lru_link);
> +    pixman_image_unref(item->image);
> +    free(item);
> +#ifndef IMAGE_CACHE_AGE
> +    cache->num_items--;
> +#endif
> +}
> +
> +#define IMAGE_CACHE_MAX_ITEMS 2
> +
> +static void image_cache_put(SpiceImageCache *spice_cache, uint64_t id, pixman_image_t *image)
> +{
> +    ImageCache *cache = (ImageCache *)spice_cache;
> +    ImageCacheItem *item;
> +
> +#ifndef IMAGE_CACHE_AGE
> +    if (cache->num_items == IMAGE_CACHE_MAX_ITEMS) {
> +        ImageCacheItem *tail = (ImageCacheItem *)ring_get_tail(&cache->lru);
> +        spice_assert(tail);
> +        image_cache_remove(cache, tail);
> +    }
> +#endif
> +
> +    item = spice_new(ImageCacheItem, 1);
> +    item->id = id;
> +#ifdef IMAGE_CACHE_AGE
> +    item->age = cache->age;
> +#else
> +    cache->num_items++;
> +#endif
> +    item->image = pixman_image_ref(image);
> +    ring_item_init(&item->lru_link);
> +
> +    item->next = cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
> +    cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE] = item;
> +
> +    ring_add(&cache->lru, &item->lru_link);
> +}
> +
> +static pixman_image_t *image_cache_get(SpiceImageCache *spice_cache, uint64_t id)
> +{
> +    ImageCache *cache = (ImageCache *)spice_cache;
> +
> +    ImageCacheItem *item = image_cache_find(cache, id);
> +    if (!item) {
> +        spice_error("not found");
> +    }
> +    return pixman_image_ref(item->image);
> +}
> +
> +void image_cache_init(ImageCache *cache)
> +{
> +    static SpiceImageCacheOps image_cache_ops = {
> +        image_cache_put,
> +        image_cache_get,
> +    };
> +
> +    cache->base.ops = &image_cache_ops;
> +    memset(cache->hash_table, 0, sizeof(cache->hash_table));
> +    ring_init(&cache->lru);
> +#ifdef IMAGE_CACHE_AGE
> +    cache->age = 0;
> +#else
> +    cache->num_items = 0;
> +#endif
> +}
> +
> +void image_cache_reset(ImageCache *cache)
> +{
> +    ImageCacheItem *item;
> +
> +    while ((item = (ImageCacheItem *)ring_get_head(&cache->lru))) {
> +        image_cache_remove(cache, item);
> +    }
> +#ifdef IMAGE_CACHE_AGE
> +    cache->age = 0;
> +#endif
> +}
> +
> +#define IMAGE_CACHE_DEPTH 4
> +
> +void image_cache_aging(ImageCache *cache)
> +{
> +#ifdef IMAGE_CACHE_AGE
> +    ImageCacheItem *item;
> +
> +    cache->age++;
> +    while ((item = (ImageCacheItem *)ring_get_tail(&cache->lru)) &&
> +           cache->age - item->age > IMAGE_CACHE_DEPTH) {
> +        image_cache_remove(cache, item);
> +    }
> +#endif
> +}
> +
> +void image_cache_localize(ImageCache *cache, SpiceImage **image_ptr,
> +                          SpiceImage *image_store, Drawable *drawable)
> +{
> +    SpiceImage *image = *image_ptr;
> +
> +    if (image == NULL) {
> +        spice_assert(drawable != NULL);
> +        spice_assert(drawable->red_drawable->self_bitmap_image != NULL);
> +        *image_ptr = drawable->red_drawable->self_bitmap_image;
> +        return;
> +    }
> +
> +    if (image_cache_hit(cache, image->descriptor.id)) {
> +        image_store->descriptor = image->descriptor;
> +        image_store->descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE;
> +        image_store->descriptor.flags = 0;
> +        *image_ptr = image_store;
> +        return;
> +    }
> +
> +    switch (image->descriptor.type) {
> +    case SPICE_IMAGE_TYPE_QUIC: {
> +        image_store->descriptor = image->descriptor;
> +        image_store->u.quic = image->u.quic;
> +        *image_ptr = image_store;
> +#ifdef IMAGE_CACHE_AGE
> +        image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
> +#else
> +        if (image_store->descriptor.width * image->descriptor.height >= 640 * 480) {
> +            image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
> +        }
> +#endif
> +        break;
> +    }
> +    case SPICE_IMAGE_TYPE_BITMAP:
> +    case SPICE_IMAGE_TYPE_SURFACE:
> +        /* nothing */
> +        break;
> +    default:
> +        spice_error("invalid image type");
> +    }
> +}
> +
> +void image_cache_localize_brush(ImageCache *cache, SpiceBrush *brush, SpiceImage *image_store)
> +{
> +    if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
> +        image_cache_localize(cache, &brush->u.pattern.pat, image_store, NULL);
> +    }
> +}
> +
> +void image_cache_localize_mask(ImageCache *cache, SpiceQMask *mask, SpiceImage *image_store)
> +{
> +    if (mask->bitmap) {
> +        image_cache_localize(cache, &mask->bitmap, image_store, NULL);
> +    }
> +}
> diff --git a/server/image-cache.h b/server/image-cache.h
> new file mode 100644
> index 0000000..d66c7ff
> --- /dev/null
> +++ b/server/image-cache.h
> @@ -0,0 +1,65 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifndef IMAGE_CACHE_H_
> +#define IMAGE_CACHE_H_
> +
> +#include <inttypes.h>
> +
> +#include "common/pixman_utils.h"
> +#include "common/canvas_base.h"
> +#include "common/ring.h"
> +
> +/* FIXME: move back to display_channel.h (once structs are private) */
> +typedef struct Drawable Drawable;
> +typedef struct DisplayChannelClient DisplayChannelClient;
> +
> +typedef struct ImageCacheItem {
> +    RingItem lru_link;
> +    uint64_t id;
> +#ifdef IMAGE_CACHE_AGE
> +    uint32_t age;
> +#endif
> +    struct ImageCacheItem *next;
> +    pixman_image_t *image;
> +} ImageCacheItem;
> +
> +#define IMAGE_CACHE_HASH_SIZE 1024
> +
> +typedef struct ImageCache {
> +    SpiceImageCache base;
> +    ImageCacheItem *hash_table[IMAGE_CACHE_HASH_SIZE];
> +    Ring lru;
> +#ifdef IMAGE_CACHE_AGE
> +    uint32_t age;
> +#else
> +    uint32_t num_items;
> +#endif
> +} ImageCache;
> +
> +int          image_cache_hit               (ImageCache *cache, uint64_t id);
> +void         image_cache_init              (ImageCache *cache);
> +void         image_cache_reset             (ImageCache *cache);
> +void         image_cache_aging             (ImageCache *cache);
> +void         image_cache_localize          (ImageCache *cache, SpiceImage **image_ptr,
> +                                            SpiceImage *image_store, Drawable *drawable);
> +void         image_cache_localize_brush    (ImageCache *cache, SpiceBrush *brush,
> +                                            SpiceImage *image_store);
> +void         image_cache_localize_mask     (ImageCache *cache, SpiceQMask *mask,
> +                                            SpiceImage *image_store);
> +
> +#endif
> diff --git a/server/inputs-channel.c b/server/inputs-channel.c
> new file mode 100644
> index 0000000..3e8fccd
> --- /dev/null
> +++ b/server/inputs-channel.c
> @@ -0,0 +1,679 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <netinet/in.h> // IPPROTO_TCP
> +#include <netinet/tcp.h> // TCP_NODELAY
> +#include <fcntl.h>
> +#include <stddef.h> // NULL
> +#include <errno.h>
> +#include <spice/macros.h>
> +#include <spice/vd_agent.h>
> +#include <spice/protocol.h>
> +#include <stdbool.h>
> +
> +#include "common/marshaller.h"
> +#include "common/messages.h"
> +#include "common/generated_server_marshallers.h"
> +
> +#include "demarshallers.h"
> +#include "spice.h"
> +#include "red_common.h"
> +#include "reds.h"
> +#include "reds_stream.h"
> +#include "red_channel.h"
> +#include "main-channel.h"
> +#include "inputs-channel.h"
> +#include "migration-protocol.h"
> +
> +// TODO: RECEIVE_BUF_SIZE used to be the same for inputs_channel and main_channel
> +// since it was defined once in reds.c which contained both.
> +// Now that they are split we can give a more fitting value for inputs - what
> +// should it be?
> +#define REDS_AGENT_WINDOW_SIZE 10
> +#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
> +
> +// approximate max receive message size
> +#define RECEIVE_BUF_SIZE \
> +    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
> +
> +struct SpiceKbdState {
> +    bool push_ext;
> +
> +    /* track key press state */
> +    bool key[0x7f];
> +    bool key_ext[0x7f];
> +};
> +
> +struct SpiceMouseState {
> +    int dummy;
> +};
> +
> +struct SpiceTabletState {
> +    int dummy;
> +};
> +
> +typedef struct InputsChannelClient {
> +    RedChannelClient base;
> +    uint16_t motion_count;
> +} InputsChannelClient;
> +
> +typedef struct InputsChannel {
> +    RedChannel base;
> +    uint8_t recv_buf[RECEIVE_BUF_SIZE];
> +    VDAgentMouseState mouse_state;
> +    int src_during_migrate;
> +} InputsChannel;
> +
> +enum {
> +    PIPE_ITEM_INPUTS_INIT = PIPE_ITEM_TYPE_CHANNEL_BASE,
> +    PIPE_ITEM_MOUSE_MOTION_ACK,
> +    PIPE_ITEM_KEY_MODIFIERS,
> +    PIPE_ITEM_MIGRATE_DATA,
> +};
> +
> +typedef struct InputsPipeItem {
> +    PipeItem base;
> +} InputsPipeItem;
> +
> +typedef struct KeyModifiersPipeItem {
> +    PipeItem base;
> +    uint8_t modifiers;
> +} KeyModifiersPipeItem;
> +
> +typedef struct InputsInitPipeItem {
> +    PipeItem base;
> +    uint8_t modifiers;
> +} InputsInitPipeItem;
> +
> +static SpiceKbdInstance *keyboard = NULL;
> +static SpiceMouseInstance *mouse = NULL;
> +static SpiceTabletInstance *tablet = NULL;
> +
> +static SpiceTimer *key_modifiers_timer;
> +
> +static InputsChannel *g_inputs_channel = NULL;
> +
> +#define KEY_MODIFIERS_TTL (1000 * 2) /*2sec*/
> +
> +#define SCROLL_LOCK_SCAN_CODE 0x46
> +#define NUM_LOCK_SCAN_CODE 0x45
> +#define CAPS_LOCK_SCAN_CODE 0x3a
> +
> +int inputs_inited(void)
> +{
> +    return !!g_inputs_channel;
> +}
> +
> +int inputs_set_keyboard(SpiceKbdInstance *_keyboard)
> +{
> +    if (keyboard) {
> +        spice_printerr("already have keyboard");
> +        return -1;
> +    }
> +    keyboard = _keyboard;
> +    keyboard->st = spice_new0(SpiceKbdState, 1);
> +    return 0;
> +}
> +
> +int inputs_set_mouse(SpiceMouseInstance *_mouse)
> +{
> +    if (mouse) {
> +        spice_printerr("already have mouse");
> +        return -1;
> +    }
> +    mouse = _mouse;
> +    mouse->st = spice_new0(SpiceMouseState, 1);
> +    return 0;
> +}
> +
> +int inputs_set_tablet(SpiceTabletInstance *_tablet)
> +{
> +    if (tablet) {
> +        spice_printerr("already have tablet");
> +        return -1;
> +    }
> +    tablet = _tablet;
> +    tablet->st = spice_new0(SpiceTabletState, 1);
> +    return 0;
> +}
> +
> +int inputs_has_tablet(void)
> +{
> +    return !!tablet;
> +}
> +
> +void inputs_detach_tablet(SpiceTabletInstance *_tablet)
> +{
> +    spice_printerr("");
> +    tablet = NULL;
> +}
> +
> +void inputs_set_tablet_logical_size(int x_res, int y_res)
> +{
> +    SpiceTabletInterface *sif;
> +
> +    sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
> +    sif->set_logical_size(tablet, x_res, y_res);
> +}
> +
> +const VDAgentMouseState *inputs_get_mouse_state(void)
> +{
> +    spice_assert(g_inputs_channel);
> +    return &g_inputs_channel->mouse_state;
> +}
> +
> +static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
> +                                                 uint16_t type,
> +                                                 uint32_t size)
> +{
> +    InputsChannel *inputs_channel = SPICE_CONTAINEROF(rcc->channel, InputsChannel, base);
> +
> +    if (size > RECEIVE_BUF_SIZE) {
> +        spice_printerr("error: too large incoming message");
> +        return NULL;
> +    }
> +    return inputs_channel->recv_buf;
> +}
> +
> +static void inputs_channel_release_msg_rcv_buf(RedChannelClient *rcc,
> +                                               uint16_t type,
> +                                               uint32_t size,
> +                                               uint8_t *msg)
> +{
> +}
> +
> +#define OUTGOING_OK 0
> +#define OUTGOING_FAILED -1
> +#define OUTGOING_BLOCKED 1
> +
> +#define RED_MOUSE_STATE_TO_LOCAL(state)     \
> +    ((state & SPICE_MOUSE_BUTTON_MASK_LEFT) |          \
> +     ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) << 1) |   \
> +     ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) >> 1))
> +
> +#define RED_MOUSE_BUTTON_STATE_TO_AGENT(state)                      \
> +    (((state & SPICE_MOUSE_BUTTON_MASK_LEFT) ? VD_AGENT_LBUTTON_MASK : 0) |    \
> +     ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) ? VD_AGENT_MBUTTON_MASK : 0) |    \
> +     ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) ? VD_AGENT_RBUTTON_MASK : 0))
> +
> +static void activate_modifiers_watch(void)
> +{
> +    core->timer_start(key_modifiers_timer, KEY_MODIFIERS_TTL);
> +}
> +
> +static void kbd_push_scan(SpiceKbdInstance *sin, uint8_t scan)
> +{
> +    SpiceKbdInterface *sif;
> +
> +    if (!sin) {
> +        return;
> +    }
> +    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceKbdInterface, base);
> +
> +    /* track XT scan code set 1 key state */
> +    if (scan == 0xe0) {
> +        sin->st->push_ext = TRUE;
> +    } else {
> +        bool *state = sin->st->push_ext ? sin->st->key : sin->st->key_ext;
> +        sin->st->push_ext = FALSE;
> +        state[scan & 0x7f] = !(scan & 0x80);
> +    }
> +
> +    sif->push_scan_freg(sin, scan);
> +}
> +
> +static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
> +{
> +    SpiceKbdInterface *sif;
> +
> +    if (!sin) {
> +        return 0;
> +    }
> +    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceKbdInterface, base);
> +    return sif->get_leds(sin);
> +}
> +
> +static PipeItem *inputs_key_modifiers_item_new(
> +    RedChannelClient *rcc, void *data, int num)
> +{
> +    KeyModifiersPipeItem *item = spice_malloc(sizeof(KeyModifiersPipeItem));
> +
> +    red_channel_pipe_item_init(rcc->channel, &item->base,
> +                               PIPE_ITEM_KEY_MODIFIERS);
> +    item->modifiers = *(uint8_t *)data;
> +    return &item->base;
> +}
> +
> +static void inputs_channel_send_migrate_data(RedChannelClient *rcc,
> +                                             SpiceMarshaller *m,
> +                                             PipeItem *item)
> +{
> +    InputsChannelClient *icc = SPICE_CONTAINEROF(rcc, InputsChannelClient, base);
> +
> +    g_inputs_channel->src_during_migrate = FALSE;
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item);
> +
> +    spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_INPUTS_MAGIC);
> +    spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_INPUTS_VERSION);
> +    spice_marshaller_add_uint16(m, icc->motion_count);
> +}
> +
> +static void inputs_channel_release_pipe_item(RedChannelClient *rcc,
> +    PipeItem *base, int item_pushed)
> +{
> +    free(base);
> +}
> +
> +static void inputs_channel_send_item(RedChannelClient *rcc, PipeItem *base)
> +{
> +    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
> +
> +    switch (base->type) {
> +        case PIPE_ITEM_KEY_MODIFIERS:
> +        {
> +            SpiceMsgInputsKeyModifiers key_modifiers;
> +
> +            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_KEY_MODIFIERS, base);
> +            key_modifiers.modifiers =
> +                SPICE_CONTAINEROF(base, KeyModifiersPipeItem, base)->modifiers;
> +            spice_marshall_msg_inputs_key_modifiers(m, &key_modifiers);
> +            break;
> +        }
> +        case PIPE_ITEM_INPUTS_INIT:
> +        {
> +            SpiceMsgInputsInit inputs_init;
> +
> +            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_INIT, base);
> +            inputs_init.keyboard_modifiers =
> +                SPICE_CONTAINEROF(base, InputsInitPipeItem, base)->modifiers;
> +            spice_marshall_msg_inputs_init(m, &inputs_init);
> +            break;
> +        }
> +        case PIPE_ITEM_MOUSE_MOTION_ACK:
> +            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_MOUSE_MOTION_ACK, base);
> +            break;
> +        case PIPE_ITEM_MIGRATE_DATA:
> +            inputs_channel_send_migrate_data(rcc, m, base);
> +            break;
> +        default:
> +            spice_warning("invalid pipe iten %d", base->type);
> +            break;
> +    }
> +    red_channel_client_begin_send_message(rcc);
> +}
> +
> +static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type,
> +                                        void *message)
> +{
> +    InputsChannel *inputs_channel = (InputsChannel *)rcc->channel;
> +    InputsChannelClient *icc = (InputsChannelClient *)rcc;
> +    uint32_t i;
> +
> +    spice_assert(g_inputs_channel == inputs_channel);
> +    switch (type) {
> +    case SPICE_MSGC_INPUTS_KEY_DOWN: {
> +        SpiceMsgcKeyDown *key_down = message;
> +        if (key_down->code == CAPS_LOCK_SCAN_CODE ||
> +            key_down->code == NUM_LOCK_SCAN_CODE ||
> +            key_down->code == SCROLL_LOCK_SCAN_CODE) {
> +            activate_modifiers_watch();
> +        }
> +    }
> +    case SPICE_MSGC_INPUTS_KEY_UP: {
> +        SpiceMsgcKeyUp *key_up = message;
> +        for (i = 0; i < 4; i++) {
> +            uint8_t code = (key_up->code >> (i * 8)) & 0xff;
> +            if (code == 0) {
> +                break;
> +            }
> +            kbd_push_scan(keyboard, code);
> +        }
> +        break;
> +    }
> +    case SPICE_MSGC_INPUTS_KEY_SCANCODE: {
> +        uint8_t *code = message;
> +        for (i = 0; i < size; i++) {
> +            kbd_push_scan(keyboard, code[i]);
> +        }
> +        break;
> +    }
> +    case SPICE_MSGC_INPUTS_MOUSE_MOTION: {
> +        SpiceMsgcMouseMotion *mouse_motion = message;
> +
> +        if (++icc->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0 &&
> +            !g_inputs_channel->src_during_migrate) {
> +            red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
> +            icc->motion_count = 0;
> +        }
> +        if (mouse && reds_get_mouse_mode() == SPICE_MOUSE_MODE_SERVER) {
> +            SpiceMouseInterface *sif;
> +            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
> +            sif->motion(mouse,
> +                        mouse_motion->dx, mouse_motion->dy, 0,
> +                        RED_MOUSE_STATE_TO_LOCAL(mouse_motion->buttons_state));
> +        }
> +        break;
> +    }
> +    case SPICE_MSGC_INPUTS_MOUSE_POSITION: {
> +        SpiceMsgcMousePosition *pos = message;
> +
> +        if (++icc->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0 &&
> +            !g_inputs_channel->src_during_migrate) {
> +            red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
> +            icc->motion_count = 0;
> +        }
> +        if (reds_get_mouse_mode() != SPICE_MOUSE_MODE_CLIENT) {
> +            break;
> +        }
> +        spice_assert((reds_get_agent_mouse() && reds_has_vdagent()) || tablet);
> +        if (!reds_get_agent_mouse() || !reds_has_vdagent()) {
> +            SpiceTabletInterface *sif;
> +            sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
> +            sif->position(tablet, pos->x, pos->y, RED_MOUSE_STATE_TO_LOCAL(pos->buttons_state));
> +            break;
> +        }
> +        VDAgentMouseState *mouse_state = &inputs_channel->mouse_state;
> +        mouse_state->x = pos->x;
> +        mouse_state->y = pos->y;
> +        mouse_state->buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(pos->buttons_state);
> +        mouse_state->display_id = pos->display_id;
> +        reds_handle_agent_mouse_event(mouse_state);
> +        break;
> +    }
> +    case SPICE_MSGC_INPUTS_MOUSE_PRESS: {
> +        SpiceMsgcMousePress *mouse_press = message;
> +        int dz = 0;
> +        if (mouse_press->button == SPICE_MOUSE_BUTTON_UP) {
> +            dz = -1;
> +        } else if (mouse_press->button == SPICE_MOUSE_BUTTON_DOWN) {
> +            dz = 1;
> +        }
> +        if (reds_get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
> +            if (reds_get_agent_mouse() && reds_has_vdagent()) {
> +                inputs_channel->mouse_state.buttons =
> +                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_press->buttons_state) |
> +                    (dz == -1 ? VD_AGENT_UBUTTON_MASK : 0) |
> +                    (dz == 1 ? VD_AGENT_DBUTTON_MASK : 0);
> +                reds_handle_agent_mouse_event(&inputs_channel->mouse_state);
> +            } else if (tablet) {
> +                SpiceTabletInterface *sif;
> +                sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
> +                sif->wheel(tablet, dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
> +            }
> +        } else if (mouse) {
> +            SpiceMouseInterface *sif;
> +            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
> +            sif->motion(mouse, 0, 0, dz,
> +                        RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
> +        }
> +        break;
> +    }
> +    case SPICE_MSGC_INPUTS_MOUSE_RELEASE: {
> +        SpiceMsgcMouseRelease *mouse_release = message;
> +        if (reds_get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
> +            if (reds_get_agent_mouse() && reds_has_vdagent()) {
> +                inputs_channel->mouse_state.buttons =
> +                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_release->buttons_state);
> +                reds_handle_agent_mouse_event(&inputs_channel->mouse_state);
> +            } else if (tablet) {
> +                SpiceTabletInterface *sif;
> +                sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
> +                sif->buttons(tablet, RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
> +            }
> +        } else if (mouse) {
> +            SpiceMouseInterface *sif;
> +            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
> +            sif->buttons(mouse,
> +                         RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
> +        }
> +        break;
> +    }
> +    case SPICE_MSGC_INPUTS_KEY_MODIFIERS: {
> +        SpiceMsgcKeyModifiers *modifiers = message;
> +        uint8_t leds;
> +
> +        if (!keyboard) {
> +            break;
> +        }
> +        leds = kbd_get_leds(keyboard);
> +        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK) !=
> +            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK)) {
> +            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE);
> +            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE | 0x80);
> +        }
> +        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK) !=
> +            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK)) {
> +            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE);
> +            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE | 0x80);
> +        }
> +        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK) !=
> +            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK)) {
> +            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE);
> +            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE | 0x80);
> +        }
> +        activate_modifiers_watch();
> +        break;
> +    }
> +    case SPICE_MSGC_DISCONNECTING:
> +        break;
> +    default:
> +        return red_channel_client_handle_message(rcc, size, type, message);
> +    }
> +    return TRUE;
> +}
> +
> +static void inputs_release_keys(void)
> +{
> +    int i;
> +    SpiceKbdState *st;
> +
> +    if (!keyboard) {
> +        return;
> +    }
> +    st = keyboard->st;
> +
> +    for (i = 0; i < SPICE_N_ELEMENTS(st->key); i++) {
> +        if (!st->key[i])
> +            continue;
> +
> +        st->key[i] = FALSE;
> +        kbd_push_scan(keyboard, i | 0x80);
> +    }
> +
> +    for (i = 0; i < SPICE_N_ELEMENTS(st->key_ext); i++) {
> +        if (!st->key_ext[i])
> +            continue;
> +
> +        st->key_ext[i] = FALSE;
> +        kbd_push_scan(keyboard, 0xe0);
> +        kbd_push_scan(keyboard, i | 0x80);
> +    }
> +}
> +
> +static void inputs_channel_on_disconnect(RedChannelClient *rcc)
> +{
> +    if (!rcc) {
> +        return;
> +    }
> +    inputs_release_keys();
> +}
> +
> +static void inputs_pipe_add_init(RedChannelClient *rcc)
> +{
> +    InputsInitPipeItem *item = spice_malloc(sizeof(InputsInitPipeItem));
> +
> +    red_channel_pipe_item_init(rcc->channel, &item->base,
> +                               PIPE_ITEM_INPUTS_INIT);
> +    item->modifiers = kbd_get_leds(keyboard);
> +    red_channel_client_pipe_add_push(rcc, &item->base);
> +}
> +
> +static int inputs_channel_config_socket(RedChannelClient *rcc)
> +{
> +    int delay_val = 1;
> +    RedsStream *stream = red_channel_client_get_stream(rcc);
> +
> +    if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY,
> +            &delay_val, sizeof(delay_val)) == -1) {
> +        if (errno != ENOTSUP && errno != ENOPROTOOPT) {
> +            spice_printerr("setsockopt failed, %s", strerror(errno));
> +            return FALSE;
> +        }
> +    }
> +
> +    return TRUE;
> +}
> +
> +static void inputs_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
> +{
> +}
> +
> +static void inputs_connect(RedChannel *channel, RedClient *client,
> +                           RedsStream *stream, int migration,
> +                           int num_common_caps, uint32_t *common_caps,
> +                           int num_caps, uint32_t *caps)
> +{
> +    InputsChannelClient *icc;
> +
> +    spice_assert(g_inputs_channel);
> +    spice_assert(channel == &g_inputs_channel->base);
> +
> +    if (!reds_stream_is_ssl(stream) && !red_client_during_migrate_at_target(client)) {
> +        main_channel_client_push_notify(red_client_get_main(client),
> +                                        "keyboard channel is insecure");
> +    }
> +
> +    spice_printerr("inputs channel client create");
> +    icc = (InputsChannelClient*)red_channel_client_create(sizeof(InputsChannelClient),
> +                                                          channel,
> +                                                          client,
> +                                                          stream,
> +                                                          FALSE,
> +                                                          num_common_caps, common_caps,
> +                                                          num_caps, caps);
> +    if (!icc) {
> +        return;
> +    }
> +    icc->motion_count = 0;
> +    inputs_pipe_add_init(&icc->base);
> +}
> +
> +static void inputs_migrate(RedChannelClient *rcc)
> +{
> +    g_inputs_channel->src_during_migrate = TRUE;
> +    red_channel_client_default_migrate(rcc);
> +}
> +
> +static void inputs_push_keyboard_modifiers(uint8_t modifiers)
> +{
> +    if (!g_inputs_channel || !red_channel_is_connected(&g_inputs_channel->base) ||
> +        g_inputs_channel->src_during_migrate) {
> +        return;
> +    }
> +    red_channel_pipes_new_add_push(&g_inputs_channel->base,
> +        inputs_key_modifiers_item_new, (void*)&modifiers);
> +}
> +
> +void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds)
> +{
> +    inputs_push_keyboard_modifiers(leds);
> +}
> +
> +static void key_modifiers_sender(void *opaque)
> +{
> +    inputs_push_keyboard_modifiers(kbd_get_leds(keyboard));
> +}
> +
> +static int inputs_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
> +{
> +    red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MIGRATE_DATA);
> +    return TRUE;
> +}
> +
> +static int inputs_channel_handle_migrate_data(RedChannelClient *rcc,
> +                                              uint32_t size,
> +                                              void *message)
> +{
> +    InputsChannelClient *icc = SPICE_CONTAINEROF(rcc, InputsChannelClient, base);
> +    SpiceMigrateDataHeader *header;
> +    SpiceMigrateDataInputs *mig_data;
> +
> +    header = (SpiceMigrateDataHeader *)message;
> +    mig_data = (SpiceMigrateDataInputs *)(header + 1);
> +
> +    if (!migration_protocol_validate_header(header,
> +                                            SPICE_MIGRATE_DATA_INPUTS_MAGIC,
> +                                            SPICE_MIGRATE_DATA_INPUTS_VERSION)) {
> +        spice_error("bad header");
> +        return FALSE;
> +    }
> +    key_modifiers_sender(NULL);
> +    icc->motion_count = mig_data->motion_count;
> +
> +    for (; icc->motion_count >= SPICE_INPUT_MOTION_ACK_BUNCH;
> +           icc->motion_count -= SPICE_INPUT_MOTION_ACK_BUNCH) {
> +        red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
> +    }
> +    return TRUE;
> +}
> +
> +void inputs_init(void)
> +{
> +    ChannelCbs channel_cbs = { NULL, };
> +    ClientCbs client_cbs = { NULL, };
> +
> +    spice_assert(!g_inputs_channel);
> +
> +    channel_cbs.config_socket = inputs_channel_config_socket;
> +    channel_cbs.on_disconnect = inputs_channel_on_disconnect;
> +    channel_cbs.send_item = inputs_channel_send_item;
> +    channel_cbs.hold_item = inputs_channel_hold_pipe_item;
> +    channel_cbs.release_item = inputs_channel_release_pipe_item;
> +    channel_cbs.alloc_recv_buf = inputs_channel_alloc_msg_rcv_buf;
> +    channel_cbs.release_recv_buf = inputs_channel_release_msg_rcv_buf;
> +    channel_cbs.handle_migrate_data = inputs_channel_handle_migrate_data;
> +    channel_cbs.handle_migrate_flush_mark = inputs_channel_handle_migrate_flush_mark;
> +
> +    g_inputs_channel = (InputsChannel *)red_channel_create_parser(
> +                                    sizeof(InputsChannel),
> +                                    core,
> +                                    SPICE_CHANNEL_INPUTS, 0,
> +                                    FALSE, /* handle_acks */
> +                                    spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL),
> +                                    inputs_channel_handle_parsed,
> +                                    &channel_cbs,
> +                                    SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER);
> +
> +    if (!g_inputs_channel) {
> +        spice_error("failed to allocate Inputs Channel");
> +    }
> +
> +    client_cbs.connect = inputs_connect;
> +    client_cbs.migrate = inputs_migrate;
> +    red_channel_register_client_cbs(&g_inputs_channel->base, &client_cbs);
> +
> +    red_channel_set_cap(&g_inputs_channel->base, SPICE_INPUTS_CAP_KEY_SCANCODE);
> +    reds_register_channel(&g_inputs_channel->base);
> +
> +    if (!(key_modifiers_timer = core->timer_add(key_modifiers_sender, NULL))) {
> +        spice_error("key modifiers timer create failed");
> +    }
> +}
> diff --git a/server/inputs-channel.h b/server/inputs-channel.h
> new file mode 100644
> index 0000000..7f7ace0
> --- /dev/null
> +++ b/server/inputs-channel.h
> @@ -0,0 +1,38 @@
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +
> +#ifndef _INPUTS_CHANNEL_H_
> +#define _INPUTS_CHANNEL_H_
> +
> +// Inputs channel, dealing with keyboard, mouse, tablet.
> +// This include should only be used by reds.c and inputs-channel.c
> +
> +#include <stdint.h>
> +#include <spice/vd_agent.h>
> +
> +void inputs_init(void);
> +int inputs_inited(void);
> +int inputs_has_tablet(void);
> +const VDAgentMouseState *inputs_get_mouse_state(void);
> +void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds);
> +int inputs_set_keyboard(SpiceKbdInstance *_keyboard);
> +int inputs_set_mouse(SpiceMouseInstance *_mouse);
> +int inputs_set_tablet(SpiceTabletInstance *_tablet);
> +void inputs_detach_tablet(SpiceTabletInstance *_tablet);
> +void inputs_set_tablet_logical_size(int x_res, int y_res);
> +
> +#endif
> diff --git a/server/inputs_channel.c b/server/inputs_channel.c
> deleted file mode 100644
> index 2934572..0000000
> --- a/server/inputs_channel.c
> +++ /dev/null
> @@ -1,679 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifdef HAVE_CONFIG_H
> -#include <config.h>
> -#endif
> -
> -#include <netinet/in.h> // IPPROTO_TCP
> -#include <netinet/tcp.h> // TCP_NODELAY
> -#include <fcntl.h>
> -#include <stddef.h> // NULL
> -#include <errno.h>
> -#include <spice/macros.h>
> -#include <spice/vd_agent.h>
> -#include <spice/protocol.h>
> -#include <stdbool.h>
> -
> -#include "common/marshaller.h"
> -#include "common/messages.h"
> -#include "common/generated_server_marshallers.h"
> -
> -#include "demarshallers.h"
> -#include "spice.h"
> -#include "red_common.h"
> -#include "reds.h"
> -#include "reds_stream.h"
> -#include "red_channel.h"
> -#include "main_channel.h"
> -#include "inputs_channel.h"
> -#include "migration_protocol.h"
> -
> -// TODO: RECEIVE_BUF_SIZE used to be the same for inputs_channel and main_channel
> -// since it was defined once in reds.c which contained both.
> -// Now that they are split we can give a more fitting value for inputs - what
> -// should it be?
> -#define REDS_AGENT_WINDOW_SIZE 10
> -#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
> -
> -// approximate max receive message size
> -#define RECEIVE_BUF_SIZE \
> -    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
> -
> -struct SpiceKbdState {
> -    bool push_ext;
> -
> -    /* track key press state */
> -    bool key[0x7f];
> -    bool key_ext[0x7f];
> -};
> -
> -struct SpiceMouseState {
> -    int dummy;
> -};
> -
> -struct SpiceTabletState {
> -    int dummy;
> -};
> -
> -typedef struct InputsChannelClient {
> -    RedChannelClient base;
> -    uint16_t motion_count;
> -} InputsChannelClient;
> -
> -typedef struct InputsChannel {
> -    RedChannel base;
> -    uint8_t recv_buf[RECEIVE_BUF_SIZE];
> -    VDAgentMouseState mouse_state;
> -    int src_during_migrate;
> -} InputsChannel;
> -
> -enum {
> -    PIPE_ITEM_INPUTS_INIT = PIPE_ITEM_TYPE_CHANNEL_BASE,
> -    PIPE_ITEM_MOUSE_MOTION_ACK,
> -    PIPE_ITEM_KEY_MODIFIERS,
> -    PIPE_ITEM_MIGRATE_DATA,
> -};
> -
> -typedef struct InputsPipeItem {
> -    PipeItem base;
> -} InputsPipeItem;
> -
> -typedef struct KeyModifiersPipeItem {
> -    PipeItem base;
> -    uint8_t modifiers;
> -} KeyModifiersPipeItem;
> -
> -typedef struct InputsInitPipeItem {
> -    PipeItem base;
> -    uint8_t modifiers;
> -} InputsInitPipeItem;
> -
> -static SpiceKbdInstance *keyboard = NULL;
> -static SpiceMouseInstance *mouse = NULL;
> -static SpiceTabletInstance *tablet = NULL;
> -
> -static SpiceTimer *key_modifiers_timer;
> -
> -static InputsChannel *g_inputs_channel = NULL;
> -
> -#define KEY_MODIFIERS_TTL (1000 * 2) /*2sec*/
> -
> -#define SCROLL_LOCK_SCAN_CODE 0x46
> -#define NUM_LOCK_SCAN_CODE 0x45
> -#define CAPS_LOCK_SCAN_CODE 0x3a
> -
> -int inputs_inited(void)
> -{
> -    return !!g_inputs_channel;
> -}
> -
> -int inputs_set_keyboard(SpiceKbdInstance *_keyboard)
> -{
> -    if (keyboard) {
> -        spice_printerr("already have keyboard");
> -        return -1;
> -    }
> -    keyboard = _keyboard;
> -    keyboard->st = spice_new0(SpiceKbdState, 1);
> -    return 0;
> -}
> -
> -int inputs_set_mouse(SpiceMouseInstance *_mouse)
> -{
> -    if (mouse) {
> -        spice_printerr("already have mouse");
> -        return -1;
> -    }
> -    mouse = _mouse;
> -    mouse->st = spice_new0(SpiceMouseState, 1);
> -    return 0;
> -}
> -
> -int inputs_set_tablet(SpiceTabletInstance *_tablet)
> -{
> -    if (tablet) {
> -        spice_printerr("already have tablet");
> -        return -1;
> -    }
> -    tablet = _tablet;
> -    tablet->st = spice_new0(SpiceTabletState, 1);
> -    return 0;
> -}
> -
> -int inputs_has_tablet(void)
> -{
> -    return !!tablet;
> -}
> -
> -void inputs_detach_tablet(SpiceTabletInstance *_tablet)
> -{
> -    spice_printerr("");
> -    tablet = NULL;
> -}
> -
> -void inputs_set_tablet_logical_size(int x_res, int y_res)
> -{
> -    SpiceTabletInterface *sif;
> -
> -    sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
> -    sif->set_logical_size(tablet, x_res, y_res);
> -}
> -
> -const VDAgentMouseState *inputs_get_mouse_state(void)
> -{
> -    spice_assert(g_inputs_channel);
> -    return &g_inputs_channel->mouse_state;
> -}
> -
> -static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
> -                                                 uint16_t type,
> -                                                 uint32_t size)
> -{
> -    InputsChannel *inputs_channel = SPICE_CONTAINEROF(rcc->channel, InputsChannel, base);
> -
> -    if (size > RECEIVE_BUF_SIZE) {
> -        spice_printerr("error: too large incoming message");
> -        return NULL;
> -    }
> -    return inputs_channel->recv_buf;
> -}
> -
> -static void inputs_channel_release_msg_rcv_buf(RedChannelClient *rcc,
> -                                               uint16_t type,
> -                                               uint32_t size,
> -                                               uint8_t *msg)
> -{
> -}
> -
> -#define OUTGOING_OK 0
> -#define OUTGOING_FAILED -1
> -#define OUTGOING_BLOCKED 1
> -
> -#define RED_MOUSE_STATE_TO_LOCAL(state)     \
> -    ((state & SPICE_MOUSE_BUTTON_MASK_LEFT) |          \
> -     ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) << 1) |   \
> -     ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) >> 1))
> -
> -#define RED_MOUSE_BUTTON_STATE_TO_AGENT(state)                      \
> -    (((state & SPICE_MOUSE_BUTTON_MASK_LEFT) ? VD_AGENT_LBUTTON_MASK : 0) |    \
> -     ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) ? VD_AGENT_MBUTTON_MASK : 0) |    \
> -     ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) ? VD_AGENT_RBUTTON_MASK : 0))
> -
> -static void activate_modifiers_watch(void)
> -{
> -    core->timer_start(key_modifiers_timer, KEY_MODIFIERS_TTL);
> -}
> -
> -static void kbd_push_scan(SpiceKbdInstance *sin, uint8_t scan)
> -{
> -    SpiceKbdInterface *sif;
> -
> -    if (!sin) {
> -        return;
> -    }
> -    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceKbdInterface, base);
> -
> -    /* track XT scan code set 1 key state */
> -    if (scan == 0xe0) {
> -        sin->st->push_ext = TRUE;
> -    } else {
> -        bool *state = sin->st->push_ext ? sin->st->key : sin->st->key_ext;
> -        sin->st->push_ext = FALSE;
> -        state[scan & 0x7f] = !(scan & 0x80);
> -    }
> -
> -    sif->push_scan_freg(sin, scan);
> -}
> -
> -static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
> -{
> -    SpiceKbdInterface *sif;
> -
> -    if (!sin) {
> -        return 0;
> -    }
> -    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceKbdInterface, base);
> -    return sif->get_leds(sin);
> -}
> -
> -static PipeItem *inputs_key_modifiers_item_new(
> -    RedChannelClient *rcc, void *data, int num)
> -{
> -    KeyModifiersPipeItem *item = spice_malloc(sizeof(KeyModifiersPipeItem));
> -
> -    red_channel_pipe_item_init(rcc->channel, &item->base,
> -                               PIPE_ITEM_KEY_MODIFIERS);
> -    item->modifiers = *(uint8_t *)data;
> -    return &item->base;
> -}
> -
> -static void inputs_channel_send_migrate_data(RedChannelClient *rcc,
> -                                             SpiceMarshaller *m,
> -                                             PipeItem *item)
> -{
> -    InputsChannelClient *icc = SPICE_CONTAINEROF(rcc, InputsChannelClient, base);
> -
> -    g_inputs_channel->src_during_migrate = FALSE;
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item);
> -
> -    spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_INPUTS_MAGIC);
> -    spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_INPUTS_VERSION);
> -    spice_marshaller_add_uint16(m, icc->motion_count);
> -}
> -
> -static void inputs_channel_release_pipe_item(RedChannelClient *rcc,
> -    PipeItem *base, int item_pushed)
> -{
> -    free(base);
> -}
> -
> -static void inputs_channel_send_item(RedChannelClient *rcc, PipeItem *base)
> -{
> -    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
> -
> -    switch (base->type) {
> -        case PIPE_ITEM_KEY_MODIFIERS:
> -        {
> -            SpiceMsgInputsKeyModifiers key_modifiers;
> -
> -            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_KEY_MODIFIERS, base);
> -            key_modifiers.modifiers =
> -                SPICE_CONTAINEROF(base, KeyModifiersPipeItem, base)->modifiers;
> -            spice_marshall_msg_inputs_key_modifiers(m, &key_modifiers);
> -            break;
> -        }
> -        case PIPE_ITEM_INPUTS_INIT:
> -        {
> -            SpiceMsgInputsInit inputs_init;
> -
> -            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_INIT, base);
> -            inputs_init.keyboard_modifiers =
> -                SPICE_CONTAINEROF(base, InputsInitPipeItem, base)->modifiers;
> -            spice_marshall_msg_inputs_init(m, &inputs_init);
> -            break;
> -        }
> -        case PIPE_ITEM_MOUSE_MOTION_ACK:
> -            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_MOUSE_MOTION_ACK, base);
> -            break;
> -        case PIPE_ITEM_MIGRATE_DATA:
> -            inputs_channel_send_migrate_data(rcc, m, base);
> -            break;
> -        default:
> -            spice_warning("invalid pipe iten %d", base->type);
> -            break;
> -    }
> -    red_channel_client_begin_send_message(rcc);
> -}
> -
> -static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type,
> -                                        void *message)
> -{
> -    InputsChannel *inputs_channel = (InputsChannel *)rcc->channel;
> -    InputsChannelClient *icc = (InputsChannelClient *)rcc;
> -    uint32_t i;
> -
> -    spice_assert(g_inputs_channel == inputs_channel);
> -    switch (type) {
> -    case SPICE_MSGC_INPUTS_KEY_DOWN: {
> -        SpiceMsgcKeyDown *key_down = message;
> -        if (key_down->code == CAPS_LOCK_SCAN_CODE ||
> -            key_down->code == NUM_LOCK_SCAN_CODE ||
> -            key_down->code == SCROLL_LOCK_SCAN_CODE) {
> -            activate_modifiers_watch();
> -        }
> -    }
> -    case SPICE_MSGC_INPUTS_KEY_UP: {
> -        SpiceMsgcKeyUp *key_up = message;
> -        for (i = 0; i < 4; i++) {
> -            uint8_t code = (key_up->code >> (i * 8)) & 0xff;
> -            if (code == 0) {
> -                break;
> -            }
> -            kbd_push_scan(keyboard, code);
> -        }
> -        break;
> -    }
> -    case SPICE_MSGC_INPUTS_KEY_SCANCODE: {
> -        uint8_t *code = message;
> -        for (i = 0; i < size; i++) {
> -            kbd_push_scan(keyboard, code[i]);
> -        }
> -        break;
> -    }
> -    case SPICE_MSGC_INPUTS_MOUSE_MOTION: {
> -        SpiceMsgcMouseMotion *mouse_motion = message;
> -
> -        if (++icc->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0 &&
> -            !g_inputs_channel->src_during_migrate) {
> -            red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
> -            icc->motion_count = 0;
> -        }
> -        if (mouse && reds_get_mouse_mode() == SPICE_MOUSE_MODE_SERVER) {
> -            SpiceMouseInterface *sif;
> -            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
> -            sif->motion(mouse,
> -                        mouse_motion->dx, mouse_motion->dy, 0,
> -                        RED_MOUSE_STATE_TO_LOCAL(mouse_motion->buttons_state));
> -        }
> -        break;
> -    }
> -    case SPICE_MSGC_INPUTS_MOUSE_POSITION: {
> -        SpiceMsgcMousePosition *pos = message;
> -
> -        if (++icc->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0 &&
> -            !g_inputs_channel->src_during_migrate) {
> -            red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
> -            icc->motion_count = 0;
> -        }
> -        if (reds_get_mouse_mode() != SPICE_MOUSE_MODE_CLIENT) {
> -            break;
> -        }
> -        spice_assert((reds_get_agent_mouse() && reds_has_vdagent()) || tablet);
> -        if (!reds_get_agent_mouse() || !reds_has_vdagent()) {
> -            SpiceTabletInterface *sif;
> -            sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
> -            sif->position(tablet, pos->x, pos->y, RED_MOUSE_STATE_TO_LOCAL(pos->buttons_state));
> -            break;
> -        }
> -        VDAgentMouseState *mouse_state = &inputs_channel->mouse_state;
> -        mouse_state->x = pos->x;
> -        mouse_state->y = pos->y;
> -        mouse_state->buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(pos->buttons_state);
> -        mouse_state->display_id = pos->display_id;
> -        reds_handle_agent_mouse_event(mouse_state);
> -        break;
> -    }
> -    case SPICE_MSGC_INPUTS_MOUSE_PRESS: {
> -        SpiceMsgcMousePress *mouse_press = message;
> -        int dz = 0;
> -        if (mouse_press->button == SPICE_MOUSE_BUTTON_UP) {
> -            dz = -1;
> -        } else if (mouse_press->button == SPICE_MOUSE_BUTTON_DOWN) {
> -            dz = 1;
> -        }
> -        if (reds_get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
> -            if (reds_get_agent_mouse() && reds_has_vdagent()) {
> -                inputs_channel->mouse_state.buttons =
> -                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_press->buttons_state) |
> -                    (dz == -1 ? VD_AGENT_UBUTTON_MASK : 0) |
> -                    (dz == 1 ? VD_AGENT_DBUTTON_MASK : 0);
> -                reds_handle_agent_mouse_event(&inputs_channel->mouse_state);
> -            } else if (tablet) {
> -                SpiceTabletInterface *sif;
> -                sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
> -                sif->wheel(tablet, dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
> -            }
> -        } else if (mouse) {
> -            SpiceMouseInterface *sif;
> -            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
> -            sif->motion(mouse, 0, 0, dz,
> -                        RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
> -        }
> -        break;
> -    }
> -    case SPICE_MSGC_INPUTS_MOUSE_RELEASE: {
> -        SpiceMsgcMouseRelease *mouse_release = message;
> -        if (reds_get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
> -            if (reds_get_agent_mouse() && reds_has_vdagent()) {
> -                inputs_channel->mouse_state.buttons =
> -                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_release->buttons_state);
> -                reds_handle_agent_mouse_event(&inputs_channel->mouse_state);
> -            } else if (tablet) {
> -                SpiceTabletInterface *sif;
> -                sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
> -                sif->buttons(tablet, RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
> -            }
> -        } else if (mouse) {
> -            SpiceMouseInterface *sif;
> -            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
> -            sif->buttons(mouse,
> -                         RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
> -        }
> -        break;
> -    }
> -    case SPICE_MSGC_INPUTS_KEY_MODIFIERS: {
> -        SpiceMsgcKeyModifiers *modifiers = message;
> -        uint8_t leds;
> -
> -        if (!keyboard) {
> -            break;
> -        }
> -        leds = kbd_get_leds(keyboard);
> -        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK) !=
> -            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK)) {
> -            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE);
> -            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE | 0x80);
> -        }
> -        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK) !=
> -            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK)) {
> -            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE);
> -            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE | 0x80);
> -        }
> -        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK) !=
> -            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK)) {
> -            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE);
> -            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE | 0x80);
> -        }
> -        activate_modifiers_watch();
> -        break;
> -    }
> -    case SPICE_MSGC_DISCONNECTING:
> -        break;
> -    default:
> -        return red_channel_client_handle_message(rcc, size, type, message);
> -    }
> -    return TRUE;
> -}
> -
> -static void inputs_release_keys(void)
> -{
> -    int i;
> -    SpiceKbdState *st;
> -
> -    if (!keyboard) {
> -        return;
> -    }
> -    st = keyboard->st;
> -
> -    for (i = 0; i < SPICE_N_ELEMENTS(st->key); i++) {
> -        if (!st->key[i])
> -            continue;
> -
> -        st->key[i] = FALSE;
> -        kbd_push_scan(keyboard, i | 0x80);
> -    }
> -
> -    for (i = 0; i < SPICE_N_ELEMENTS(st->key_ext); i++) {
> -        if (!st->key_ext[i])
> -            continue;
> -
> -        st->key_ext[i] = FALSE;
> -        kbd_push_scan(keyboard, 0xe0);
> -        kbd_push_scan(keyboard, i | 0x80);
> -    }
> -}
> -
> -static void inputs_channel_on_disconnect(RedChannelClient *rcc)
> -{
> -    if (!rcc) {
> -        return;
> -    }
> -    inputs_release_keys();
> -}
> -
> -static void inputs_pipe_add_init(RedChannelClient *rcc)
> -{
> -    InputsInitPipeItem *item = spice_malloc(sizeof(InputsInitPipeItem));
> -
> -    red_channel_pipe_item_init(rcc->channel, &item->base,
> -                               PIPE_ITEM_INPUTS_INIT);
> -    item->modifiers = kbd_get_leds(keyboard);
> -    red_channel_client_pipe_add_push(rcc, &item->base);
> -}
> -
> -static int inputs_channel_config_socket(RedChannelClient *rcc)
> -{
> -    int delay_val = 1;
> -    RedsStream *stream = red_channel_client_get_stream(rcc);
> -
> -    if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY,
> -            &delay_val, sizeof(delay_val)) == -1) {
> -        if (errno != ENOTSUP && errno != ENOPROTOOPT) {
> -            spice_printerr("setsockopt failed, %s", strerror(errno));
> -            return FALSE;
> -        }
> -    }
> -
> -    return TRUE;
> -}
> -
> -static void inputs_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
> -{
> -}
> -
> -static void inputs_connect(RedChannel *channel, RedClient *client,
> -                           RedsStream *stream, int migration,
> -                           int num_common_caps, uint32_t *common_caps,
> -                           int num_caps, uint32_t *caps)
> -{
> -    InputsChannelClient *icc;
> -
> -    spice_assert(g_inputs_channel);
> -    spice_assert(channel == &g_inputs_channel->base);
> -
> -    if (!reds_stream_is_ssl(stream) && !red_client_during_migrate_at_target(client)) {
> -        main_channel_client_push_notify(red_client_get_main(client),
> -                                        "keyboard channel is insecure");
> -    }
> -
> -    spice_printerr("inputs channel client create");
> -    icc = (InputsChannelClient*)red_channel_client_create(sizeof(InputsChannelClient),
> -                                                          channel,
> -                                                          client,
> -                                                          stream,
> -                                                          FALSE,
> -                                                          num_common_caps, common_caps,
> -                                                          num_caps, caps);
> -    if (!icc) {
> -        return;
> -    }
> -    icc->motion_count = 0;
> -    inputs_pipe_add_init(&icc->base);
> -}
> -
> -static void inputs_migrate(RedChannelClient *rcc)
> -{
> -    g_inputs_channel->src_during_migrate = TRUE;
> -    red_channel_client_default_migrate(rcc);
> -}
> -
> -static void inputs_push_keyboard_modifiers(uint8_t modifiers)
> -{
> -    if (!g_inputs_channel || !red_channel_is_connected(&g_inputs_channel->base) ||
> -        g_inputs_channel->src_during_migrate) {
> -        return;
> -    }
> -    red_channel_pipes_new_add_push(&g_inputs_channel->base,
> -        inputs_key_modifiers_item_new, (void*)&modifiers);
> -}
> -
> -void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds)
> -{
> -    inputs_push_keyboard_modifiers(leds);
> -}
> -
> -static void key_modifiers_sender(void *opaque)
> -{
> -    inputs_push_keyboard_modifiers(kbd_get_leds(keyboard));
> -}
> -
> -static int inputs_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
> -{
> -    red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MIGRATE_DATA);
> -    return TRUE;
> -}
> -
> -static int inputs_channel_handle_migrate_data(RedChannelClient *rcc,
> -                                              uint32_t size,
> -                                              void *message)
> -{
> -    InputsChannelClient *icc = SPICE_CONTAINEROF(rcc, InputsChannelClient, base);
> -    SpiceMigrateDataHeader *header;
> -    SpiceMigrateDataInputs *mig_data;
> -
> -    header = (SpiceMigrateDataHeader *)message;
> -    mig_data = (SpiceMigrateDataInputs *)(header + 1);
> -
> -    if (!migration_protocol_validate_header(header,
> -                                            SPICE_MIGRATE_DATA_INPUTS_MAGIC,
> -                                            SPICE_MIGRATE_DATA_INPUTS_VERSION)) {
> -        spice_error("bad header");
> -        return FALSE;
> -    }
> -    key_modifiers_sender(NULL);
> -    icc->motion_count = mig_data->motion_count;
> -
> -    for (; icc->motion_count >= SPICE_INPUT_MOTION_ACK_BUNCH;
> -           icc->motion_count -= SPICE_INPUT_MOTION_ACK_BUNCH) {
> -        red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
> -    }
> -    return TRUE;
> -}
> -
> -void inputs_init(void)
> -{
> -    ChannelCbs channel_cbs = { NULL, };
> -    ClientCbs client_cbs = { NULL, };
> -
> -    spice_assert(!g_inputs_channel);
> -
> -    channel_cbs.config_socket = inputs_channel_config_socket;
> -    channel_cbs.on_disconnect = inputs_channel_on_disconnect;
> -    channel_cbs.send_item = inputs_channel_send_item;
> -    channel_cbs.hold_item = inputs_channel_hold_pipe_item;
> -    channel_cbs.release_item = inputs_channel_release_pipe_item;
> -    channel_cbs.alloc_recv_buf = inputs_channel_alloc_msg_rcv_buf;
> -    channel_cbs.release_recv_buf = inputs_channel_release_msg_rcv_buf;
> -    channel_cbs.handle_migrate_data = inputs_channel_handle_migrate_data;
> -    channel_cbs.handle_migrate_flush_mark = inputs_channel_handle_migrate_flush_mark;
> -
> -    g_inputs_channel = (InputsChannel *)red_channel_create_parser(
> -                                    sizeof(InputsChannel),
> -                                    core,
> -                                    SPICE_CHANNEL_INPUTS, 0,
> -                                    FALSE, /* handle_acks */
> -                                    spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL),
> -                                    inputs_channel_handle_parsed,
> -                                    &channel_cbs,
> -                                    SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER);
> -
> -    if (!g_inputs_channel) {
> -        spice_error("failed to allocate Inputs Channel");
> -    }
> -
> -    client_cbs.connect = inputs_connect;
> -    client_cbs.migrate = inputs_migrate;
> -    red_channel_register_client_cbs(&g_inputs_channel->base, &client_cbs);
> -
> -    red_channel_set_cap(&g_inputs_channel->base, SPICE_INPUTS_CAP_KEY_SCANCODE);
> -    reds_register_channel(&g_inputs_channel->base);
> -
> -    if (!(key_modifiers_timer = core->timer_add(key_modifiers_sender, NULL))) {
> -        spice_error("key modifiers timer create failed");
> -    }
> -}
> diff --git a/server/inputs_channel.h b/server/inputs_channel.h
> deleted file mode 100644
> index 672ca83..0000000
> --- a/server/inputs_channel.h
> +++ /dev/null
> @@ -1,38 +0,0 @@
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -
> -#ifndef _INPUTS_CHANNEL_H_
> -#define _INPUTS_CHANNEL_H_
> -
> -// Inputs channel, dealing with keyboard, mouse, tablet.
> -// This include should only be used by reds.c and inputs_channel.c
> -
> -#include <stdint.h>
> -#include <spice/vd_agent.h>
> -
> -void inputs_init(void);
> -int inputs_inited(void);
> -int inputs_has_tablet(void);
> -const VDAgentMouseState *inputs_get_mouse_state(void);
> -void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds);
> -int inputs_set_keyboard(SpiceKbdInstance *_keyboard);
> -int inputs_set_mouse(SpiceMouseInstance *_mouse);
> -int inputs_set_tablet(SpiceTabletInstance *_tablet);
> -void inputs_detach_tablet(SpiceTabletInstance *_tablet);
> -void inputs_set_tablet_logical_size(int x_res, int y_res);
> -
> -#endif
> diff --git a/server/jpeg-encoder.c b/server/jpeg-encoder.c
> new file mode 100644
> index 0000000..8e54dd2
> --- /dev/null
> +++ b/server/jpeg-encoder.c
> @@ -0,0 +1,248 @@
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include "red_common.h"
> +#include "jpeg-encoder.h"
> +#include <jpeglib.h>
> +
> +typedef struct JpegEncoder {
> +    JpegEncoderUsrContext *usr;
> +
> +    struct jpeg_destination_mgr dest_mgr;
> +    struct jpeg_compress_struct cinfo;
> +    struct jpeg_error_mgr jerr;
> +
> +    struct {
> +        JpegEncoderImageType type;
> +        int width;
> +        int height;
> +        int stride;
> +        unsigned int out_size;
> +        void (*convert_line_to_RGB24) (void *line, int width, uint8_t **out_line);
> +    } cur_image;
> +} JpegEncoder;
> +
> +/* jpeg destination manager callbacks */
> +
> +static void dest_mgr_init_destination(j_compress_ptr cinfo)
> +{
> +    JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
> +    if (enc->dest_mgr.free_in_buffer == 0) {
> +        enc->dest_mgr.free_in_buffer = enc->usr->more_space(enc->usr,
> +                                                            &enc->dest_mgr.next_output_byte);
> +
> +        if (enc->dest_mgr.free_in_buffer == 0) {
> +            spice_error("not enough space");
> +        }
> +    }
> +
> +    enc->cur_image.out_size = enc->dest_mgr.free_in_buffer;
> +}
> +
> +static boolean dest_mgr_empty_output_buffer(j_compress_ptr cinfo)
> +{
> +    JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
> +    enc->dest_mgr.free_in_buffer = enc->usr->more_space(enc->usr,
> +                                                        &enc->dest_mgr.next_output_byte);
> +
> +    if (enc->dest_mgr.free_in_buffer == 0) {
> +        spice_error("not enough space");
> +    }
> +    enc->cur_image.out_size += enc->dest_mgr.free_in_buffer;
> +    return TRUE;
> +}
> +
> +static void dest_mgr_term_destination(j_compress_ptr cinfo)
> +{
> +    JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
> +    enc->cur_image.out_size -= enc->dest_mgr.free_in_buffer;
> +}
> +
> +JpegEncoderContext* jpeg_encoder_create(JpegEncoderUsrContext *usr)
> +{
> +    JpegEncoder *enc;
> +    if (!usr->more_space || !usr->more_lines) {
> +        return NULL;
> +    }
> +
> +    enc = spice_new0(JpegEncoder, 1);
> +
> +    enc->usr = usr;
> +
> +    enc->dest_mgr.init_destination = dest_mgr_init_destination;
> +    enc->dest_mgr.empty_output_buffer = dest_mgr_empty_output_buffer;
> +    enc->dest_mgr.term_destination = dest_mgr_term_destination;
> +
> +    enc->cinfo.err = jpeg_std_error(&enc->jerr);
> +
> +    jpeg_create_compress(&enc->cinfo);
> +    enc->cinfo.client_data = enc;
> +    enc->cinfo.dest = &enc->dest_mgr;
> +    return (JpegEncoderContext*)enc;
> +}
> +
> +void jpeg_encoder_destroy(JpegEncoderContext* encoder)
> +{
> +    jpeg_destroy_compress(&((JpegEncoder*)encoder)->cinfo);
> +    free(encoder);
> +}
> +
> +static void convert_RGB16_to_RGB24(void *line, int width, uint8_t **out_line)
> +{
> +    uint16_t *src_line = line;
> +    uint8_t *out_pix;
> +    int x;
> +
> +    spice_assert(out_line && *out_line);
> +
> +    out_pix = *out_line;
> +
> +    for (x = 0; x < width; x++) {
> +       uint16_t pixel = *src_line++;
> +       *out_pix++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
> +       *out_pix++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
> +       *out_pix++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
> +   }
> +}
> +
> +static void convert_BGR24_to_RGB24(void *in_line, int width, uint8_t **out_line)
> +{
> +    int x;
> +    uint8_t *out_pix;
> +    uint8_t *line = in_line;
> +    spice_assert(out_line && *out_line);
> +
> +    out_pix = *out_line;
> +
> +    for (x = 0; x < width; x++) {
> +        *out_pix++ = line[2];
> +        *out_pix++ = line[1];
> +        *out_pix++ = line[0];
> +        line += 3;
> +    }
> +}
> +
> +static void convert_BGRX32_to_RGB24(void *line, int width, uint8_t **out_line)
> +{
> +    uint32_t *src_line = line;
> +    uint8_t *out_pix;
> +    int x;
> +
> +    spice_assert(out_line && *out_line);
> +
> +    out_pix = *out_line;
> +
> +    for (x = 0; x < width; x++) {
> +        uint32_t pixel = *src_line++;
> +        *out_pix++ = (pixel >> 16) & 0xff;
> +        *out_pix++ = (pixel >> 8) & 0xff;
> +        *out_pix++ = pixel & 0xff;
> +    }
> +}
> +
> +static void convert_RGB24_to_RGB24(void *line, int width, uint8_t **out_line)
> +{
> +    *out_line = line;
> +}
> +
> +
> +#define FILL_LINES() {                                                  \
> +    if (lines == lines_end) {                                           \
> +        int n = jpeg->usr->more_lines(jpeg->usr, &lines);               \
> +        if (n <= 0) {                                                   \
> +            spice_error("more lines failed");                           \
> +        }                                                               \
> +        lines_end = lines + n * stride;                                 \
> +    }                                                                   \
> +}
> +
> +static void do_jpeg_encode(JpegEncoder *jpeg, uint8_t *lines, unsigned int num_lines)
> +{
> +    uint8_t *lines_end;
> +    uint8_t *RGB24_line;
> +    int stride, width;
> +    JSAMPROW row_pointer[1];
> +    width = jpeg->cur_image.width;
> +    stride = jpeg->cur_image.stride;
> +
> +    if (jpeg->cur_image.type != JPEG_IMAGE_TYPE_RGB24) {
> +        RGB24_line = (uint8_t *)spice_malloc(width*3);
> +    }
> +
> +    lines_end = lines + (stride * num_lines);
> +
> +    for (;jpeg->cinfo.next_scanline < jpeg->cinfo.image_height; lines += stride) {
> +        FILL_LINES();
> +        jpeg->cur_image.convert_line_to_RGB24(lines, width, &RGB24_line);
> +        row_pointer[0] = RGB24_line;
> +        jpeg_write_scanlines(&jpeg->cinfo, row_pointer, 1);
> +    }
> +
> +    if (jpeg->cur_image.type != JPEG_IMAGE_TYPE_RGB24) {
> +        free(RGB24_line);
> +    }
> +}
> +
> +int jpeg_encode(JpegEncoderContext *jpeg, int quality, JpegEncoderImageType type,
> +                int width, int height, uint8_t *lines, unsigned int num_lines, int stride,
> +                uint8_t *io_ptr, unsigned int num_io_bytes)
> +{
> +    JpegEncoder *enc = (JpegEncoder *)jpeg;
> +
> +    enc->cur_image.type = type;
> +    enc->cur_image.width = width;
> +    enc->cur_image.height = height;
> +    enc->cur_image.stride = stride;
> +    enc->cur_image.out_size = 0;
> +
> +    switch (type) {
> +    case JPEG_IMAGE_TYPE_RGB16:
> +        enc->cur_image.convert_line_to_RGB24 = convert_RGB16_to_RGB24;
> +        break;
> +    case JPEG_IMAGE_TYPE_RGB24:
> +        enc->cur_image.convert_line_to_RGB24 = convert_RGB24_to_RGB24;
> +        break;
> +    case JPEG_IMAGE_TYPE_BGR24:
> +        enc->cur_image.convert_line_to_RGB24 = convert_BGR24_to_RGB24;
> +        break;
> +    case JPEG_IMAGE_TYPE_BGRX32:
> +        enc->cur_image.convert_line_to_RGB24 = convert_BGRX32_to_RGB24;
> +        break;
> +    default:
> +        spice_error("bad image type");
> +    }
> +
> +    enc->cinfo.image_width = width;
> +    enc->cinfo.image_height = height;
> +    enc->cinfo.input_components = 3;
> +    enc->cinfo.in_color_space = JCS_RGB;
> +    jpeg_set_defaults(&enc->cinfo);
> +    jpeg_set_quality(&enc->cinfo, quality, TRUE);
> +
> +    enc->dest_mgr.next_output_byte = io_ptr;
> +    enc->dest_mgr.free_in_buffer = num_io_bytes;
> +
> +    jpeg_start_compress(&enc->cinfo, TRUE);
> +
> +    do_jpeg_encode(enc, lines, num_lines);
> +
> +    jpeg_finish_compress(&enc->cinfo);
> +    return enc->cur_image.out_size;
> +}
> diff --git a/server/jpeg-encoder.h b/server/jpeg-encoder.h
> new file mode 100644
> index 0000000..690a029
> --- /dev/null
> +++ b/server/jpeg-encoder.h
> @@ -0,0 +1,61 @@
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   Redistribution and use in source and binary forms, with or without
> +   modification, are permitted provided that the following conditions are
> +   met:
> +
> +       * Redistributions of source code must retain the above copyright
> +         notice, this list of conditions and the following disclaimer.
> +       * Redistributions in binary form must reproduce the above copyright
> +         notice, this list of conditions and the following disclaimer in
> +         the documentation and/or other materials provided with the
> +         distribution.
> +       * Neither the name of the copyright holder nor the names of its
> +         contributors may be used to endorse or promote products derived
> +         from this software without specific prior written permission.
> +
> +   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
> +   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
> +   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
> +   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> +   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> +   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> +   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> +   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> +   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> +   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> +   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> +*/
> +#ifndef _H_JPEG_ENCODER
> +#define _H_JPEG_ENCODER
> +
> +#include <spice/types.h>
> +
> +typedef enum {
> +    JPEG_IMAGE_TYPE_INVALID,
> +    JPEG_IMAGE_TYPE_RGB16,
> +    /* in byte per color types, the notation is according to the order of the
> +       colors in the memory */
> +    JPEG_IMAGE_TYPE_RGB24,
> +    JPEG_IMAGE_TYPE_BGR24,
> +    JPEG_IMAGE_TYPE_BGRX32,
> +} JpegEncoderImageType;
> +
> +typedef void* JpegEncoderContext;
> +typedef struct JpegEncoderUsrContext JpegEncoderUsrContext;
> +
> +struct JpegEncoderUsrContext {
> +    int (*more_space)(JpegEncoderUsrContext *usr, uint8_t **io_ptr);
> +    int (*more_lines)(JpegEncoderUsrContext *usr, uint8_t **lines);
> +};
> +
> +JpegEncoderContext* jpeg_encoder_create(JpegEncoderUsrContext *usr);
> +void jpeg_encoder_destroy(JpegEncoderContext *encoder);
> +
> +/* returns the total size of the encoded data. Images must be supplied from the
> +   top line to the bottom */
> +int jpeg_encode(JpegEncoderContext *jpeg, int quality, JpegEncoderImageType type,
> +                int width, int height, uint8_t *lines, unsigned int num_lines, int stride,
> +                uint8_t *io_ptr, unsigned int num_io_bytes);
> +#endif
> diff --git a/server/jpeg_encoder.c b/server/jpeg_encoder.c
> deleted file mode 100644
> index 0296e9b..0000000
> --- a/server/jpeg_encoder.c
> +++ /dev/null
> @@ -1,248 +0,0 @@
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifdef HAVE_CONFIG_H
> -#include <config.h>
> -#endif
> -
> -#include "red_common.h"
> -#include "jpeg_encoder.h"
> -#include <jpeglib.h>
> -
> -typedef struct JpegEncoder {
> -    JpegEncoderUsrContext *usr;
> -
> -    struct jpeg_destination_mgr dest_mgr;
> -    struct jpeg_compress_struct cinfo;
> -    struct jpeg_error_mgr jerr;
> -
> -    struct {
> -        JpegEncoderImageType type;
> -        int width;
> -        int height;
> -        int stride;
> -        unsigned int out_size;
> -        void (*convert_line_to_RGB24) (void *line, int width, uint8_t **out_line);
> -    } cur_image;
> -} JpegEncoder;
> -
> -/* jpeg destination manager callbacks */
> -
> -static void dest_mgr_init_destination(j_compress_ptr cinfo)
> -{
> -    JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
> -    if (enc->dest_mgr.free_in_buffer == 0) {
> -        enc->dest_mgr.free_in_buffer = enc->usr->more_space(enc->usr,
> -                                                            &enc->dest_mgr.next_output_byte);
> -
> -        if (enc->dest_mgr.free_in_buffer == 0) {
> -            spice_error("not enough space");
> -        }
> -    }
> -
> -    enc->cur_image.out_size = enc->dest_mgr.free_in_buffer;
> -}
> -
> -static boolean dest_mgr_empty_output_buffer(j_compress_ptr cinfo)
> -{
> -    JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
> -    enc->dest_mgr.free_in_buffer = enc->usr->more_space(enc->usr,
> -                                                        &enc->dest_mgr.next_output_byte);
> -
> -    if (enc->dest_mgr.free_in_buffer == 0) {
> -        spice_error("not enough space");
> -    }
> -    enc->cur_image.out_size += enc->dest_mgr.free_in_buffer;
> -    return TRUE;
> -}
> -
> -static void dest_mgr_term_destination(j_compress_ptr cinfo)
> -{
> -    JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
> -    enc->cur_image.out_size -= enc->dest_mgr.free_in_buffer;
> -}
> -
> -JpegEncoderContext* jpeg_encoder_create(JpegEncoderUsrContext *usr)
> -{
> -    JpegEncoder *enc;
> -    if (!usr->more_space || !usr->more_lines) {
> -        return NULL;
> -    }
> -
> -    enc = spice_new0(JpegEncoder, 1);
> -
> -    enc->usr = usr;
> -
> -    enc->dest_mgr.init_destination = dest_mgr_init_destination;
> -    enc->dest_mgr.empty_output_buffer = dest_mgr_empty_output_buffer;
> -    enc->dest_mgr.term_destination = dest_mgr_term_destination;
> -
> -    enc->cinfo.err = jpeg_std_error(&enc->jerr);
> -
> -    jpeg_create_compress(&enc->cinfo);
> -    enc->cinfo.client_data = enc;
> -    enc->cinfo.dest = &enc->dest_mgr;
> -    return (JpegEncoderContext*)enc;
> -}
> -
> -void jpeg_encoder_destroy(JpegEncoderContext* encoder)
> -{
> -    jpeg_destroy_compress(&((JpegEncoder*)encoder)->cinfo);
> -    free(encoder);
> -}
> -
> -static void convert_RGB16_to_RGB24(void *line, int width, uint8_t **out_line)
> -{
> -    uint16_t *src_line = line;
> -    uint8_t *out_pix;
> -    int x;
> -
> -    spice_assert(out_line && *out_line);
> -
> -    out_pix = *out_line;
> -
> -    for (x = 0; x < width; x++) {
> -       uint16_t pixel = *src_line++;
> -       *out_pix++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
> -       *out_pix++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
> -       *out_pix++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
> -   }
> -}
> -
> -static void convert_BGR24_to_RGB24(void *in_line, int width, uint8_t **out_line)
> -{
> -    int x;
> -    uint8_t *out_pix;
> -    uint8_t *line = in_line;
> -    spice_assert(out_line && *out_line);
> -
> -    out_pix = *out_line;
> -
> -    for (x = 0; x < width; x++) {
> -        *out_pix++ = line[2];
> -        *out_pix++ = line[1];
> -        *out_pix++ = line[0];
> -        line += 3;
> -    }
> -}
> -
> -static void convert_BGRX32_to_RGB24(void *line, int width, uint8_t **out_line)
> -{
> -    uint32_t *src_line = line;
> -    uint8_t *out_pix;
> -    int x;
> -
> -    spice_assert(out_line && *out_line);
> -
> -    out_pix = *out_line;
> -
> -    for (x = 0; x < width; x++) {
> -        uint32_t pixel = *src_line++;
> -        *out_pix++ = (pixel >> 16) & 0xff;
> -        *out_pix++ = (pixel >> 8) & 0xff;
> -        *out_pix++ = pixel & 0xff;
> -    }
> -}
> -
> -static void convert_RGB24_to_RGB24(void *line, int width, uint8_t **out_line)
> -{
> -    *out_line = line;
> -}
> -
> -
> -#define FILL_LINES() {                                                  \
> -    if (lines == lines_end) {                                           \
> -        int n = jpeg->usr->more_lines(jpeg->usr, &lines);               \
> -        if (n <= 0) {                                                   \
> -            spice_error("more lines failed");                           \
> -        }                                                               \
> -        lines_end = lines + n * stride;                                 \
> -    }                                                                   \
> -}
> -
> -static void do_jpeg_encode(JpegEncoder *jpeg, uint8_t *lines, unsigned int num_lines)
> -{
> -    uint8_t *lines_end;
> -    uint8_t *RGB24_line;
> -    int stride, width;
> -    JSAMPROW row_pointer[1];
> -    width = jpeg->cur_image.width;
> -    stride = jpeg->cur_image.stride;
> -
> -    if (jpeg->cur_image.type != JPEG_IMAGE_TYPE_RGB24) {
> -        RGB24_line = (uint8_t *)spice_malloc(width*3);
> -    }
> -
> -    lines_end = lines + (stride * num_lines);
> -
> -    for (;jpeg->cinfo.next_scanline < jpeg->cinfo.image_height; lines += stride) {
> -        FILL_LINES();
> -        jpeg->cur_image.convert_line_to_RGB24(lines, width, &RGB24_line);
> -        row_pointer[0] = RGB24_line;
> -        jpeg_write_scanlines(&jpeg->cinfo, row_pointer, 1);
> -    }
> -
> -    if (jpeg->cur_image.type != JPEG_IMAGE_TYPE_RGB24) {
> -        free(RGB24_line);
> -    }
> -}
> -
> -int jpeg_encode(JpegEncoderContext *jpeg, int quality, JpegEncoderImageType type,
> -                int width, int height, uint8_t *lines, unsigned int num_lines, int stride,
> -                uint8_t *io_ptr, unsigned int num_io_bytes)
> -{
> -    JpegEncoder *enc = (JpegEncoder *)jpeg;
> -
> -    enc->cur_image.type = type;
> -    enc->cur_image.width = width;
> -    enc->cur_image.height = height;
> -    enc->cur_image.stride = stride;
> -    enc->cur_image.out_size = 0;
> -
> -    switch (type) {
> -    case JPEG_IMAGE_TYPE_RGB16:
> -        enc->cur_image.convert_line_to_RGB24 = convert_RGB16_to_RGB24;
> -        break;
> -    case JPEG_IMAGE_TYPE_RGB24:
> -        enc->cur_image.convert_line_to_RGB24 = convert_RGB24_to_RGB24;
> -        break;
> -    case JPEG_IMAGE_TYPE_BGR24:
> -        enc->cur_image.convert_line_to_RGB24 = convert_BGR24_to_RGB24;
> -        break;
> -    case JPEG_IMAGE_TYPE_BGRX32:
> -        enc->cur_image.convert_line_to_RGB24 = convert_BGRX32_to_RGB24;
> -        break;
> -    default:
> -        spice_error("bad image type");
> -    }
> -
> -    enc->cinfo.image_width = width;
> -    enc->cinfo.image_height = height;
> -    enc->cinfo.input_components = 3;
> -    enc->cinfo.in_color_space = JCS_RGB;
> -    jpeg_set_defaults(&enc->cinfo);
> -    jpeg_set_quality(&enc->cinfo, quality, TRUE);
> -
> -    enc->dest_mgr.next_output_byte = io_ptr;
> -    enc->dest_mgr.free_in_buffer = num_io_bytes;
> -
> -    jpeg_start_compress(&enc->cinfo, TRUE);
> -
> -    do_jpeg_encode(enc, lines, num_lines);
> -
> -    jpeg_finish_compress(&enc->cinfo);
> -    return enc->cur_image.out_size;
> -}
> diff --git a/server/jpeg_encoder.h b/server/jpeg_encoder.h
> deleted file mode 100644
> index 690a029..0000000
> --- a/server/jpeg_encoder.h
> +++ /dev/null
> @@ -1,61 +0,0 @@
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   Redistribution and use in source and binary forms, with or without
> -   modification, are permitted provided that the following conditions are
> -   met:
> -
> -       * Redistributions of source code must retain the above copyright
> -         notice, this list of conditions and the following disclaimer.
> -       * Redistributions in binary form must reproduce the above copyright
> -         notice, this list of conditions and the following disclaimer in
> -         the documentation and/or other materials provided with the
> -         distribution.
> -       * Neither the name of the copyright holder nor the names of its
> -         contributors may be used to endorse or promote products derived
> -         from this software without specific prior written permission.
> -
> -   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
> -   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
> -   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
> -   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> -   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> -   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> -   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> -   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> -   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> -   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> -   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> -*/
> -#ifndef _H_JPEG_ENCODER
> -#define _H_JPEG_ENCODER
> -
> -#include <spice/types.h>
> -
> -typedef enum {
> -    JPEG_IMAGE_TYPE_INVALID,
> -    JPEG_IMAGE_TYPE_RGB16,
> -    /* in byte per color types, the notation is according to the order of the
> -       colors in the memory */
> -    JPEG_IMAGE_TYPE_RGB24,
> -    JPEG_IMAGE_TYPE_BGR24,
> -    JPEG_IMAGE_TYPE_BGRX32,
> -} JpegEncoderImageType;
> -
> -typedef void* JpegEncoderContext;
> -typedef struct JpegEncoderUsrContext JpegEncoderUsrContext;
> -
> -struct JpegEncoderUsrContext {
> -    int (*more_space)(JpegEncoderUsrContext *usr, uint8_t **io_ptr);
> -    int (*more_lines)(JpegEncoderUsrContext *usr, uint8_t **lines);
> -};
> -
> -JpegEncoderContext* jpeg_encoder_create(JpegEncoderUsrContext *usr);
> -void jpeg_encoder_destroy(JpegEncoderContext *encoder);
> -
> -/* returns the total size of the encoded data. Images must be supplied from the
> -   top line to the bottom */
> -int jpeg_encode(JpegEncoderContext *jpeg, int quality, JpegEncoderImageType type,
> -                int width, int height, uint8_t *lines, unsigned int num_lines, int stride,
> -                uint8_t *io_ptr, unsigned int num_io_bytes);
> -#endif
> diff --git a/server/main-channel.c b/server/main-channel.c
> new file mode 100644
> index 0000000..5ca5bba
> --- /dev/null
> +++ b/server/main-channel.c
> @@ -0,0 +1,1345 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <inttypes.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
> +#include <netinet/tcp.h>
> +#include <arpa/inet.h>
> +#include <netdb.h>
> +#include <limits.h>
> +#include <time.h>
> +#include <pthread.h>
> +#include <sys/mman.h>
> +#include <fcntl.h>
> +#include <errno.h>
> +#include <ctype.h>
> +
> +#include "common/generated_server_marshallers.h"
> +#include "common/messages.h"
> +#include "common/ring.h"
> +
> +#include "demarshallers.h"
> +#include "main-channel.h"
> +#include "red_channel.h"
> +#include "red_common.h"
> +#include "reds.h"
> +#include "migration-protocol.h"
> +#include "main-dispatcher.h"
> +#include "utils.h"
> +
> +#define ZERO_BUF_SIZE 4096
> +
> +#define NET_TEST_WARMUP_BYTES 0
> +#define NET_TEST_BYTES (1024 * 250)
> +
> +#define PING_INTERVAL (1000 * 10)
> +
> +#define CLIENT_CONNECTIVITY_TIMEOUT (30*1000) // 30 seconds
> +
> +static uint8_t zero_page[ZERO_BUF_SIZE] = {0};
> +
> +enum {
> +    PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = PIPE_ITEM_TYPE_CHANNEL_BASE,
> +    PIPE_ITEM_TYPE_MAIN_PING,
> +    PIPE_ITEM_TYPE_MAIN_MOUSE_MODE,
> +    PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED,
> +    PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN,
> +    PIPE_ITEM_TYPE_MAIN_AGENT_DATA,
> +    PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA,
> +    PIPE_ITEM_TYPE_MAIN_INIT,
> +    PIPE_ITEM_TYPE_MAIN_NOTIFY,
> +    PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN,
> +    PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS,
> +    PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST,
> +    PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME,
> +    PIPE_ITEM_TYPE_MAIN_NAME,
> +    PIPE_ITEM_TYPE_MAIN_UUID,
> +    PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
> +};
> +
> +typedef struct RefsPipeItem {
> +    PipeItem base;
> +    int *refs;
> +} RefsPipeItem;
> +
> +typedef struct PingPipeItem {
> +    PipeItem base;
> +    int size;
> +} PingPipeItem;
> +
> +typedef struct MouseModePipeItem {
> +    PipeItem base;
> +    int current_mode;
> +    int is_client_mouse_allowed;
> +} MouseModePipeItem;
> +
> +typedef struct TokensPipeItem {
> +    PipeItem base;
> +    int tokens;
> +} TokensPipeItem;
> +
> +typedef struct AgentDataPipeItem {
> +    PipeItem base;
> +    uint8_t* data;
> +    size_t len;
> +    spice_marshaller_item_free_func free_data;
> +    void *opaque;
> +} AgentDataPipeItem;
> +
> +typedef struct InitPipeItem {
> +    PipeItem base;
> +    int connection_id;
> +    int display_channels_hint;
> +    int current_mouse_mode;
> +    int is_client_mouse_allowed;
> +    int multi_media_time;
> +    int ram_hint;
> +} InitPipeItem;
> +
> +typedef struct NamePipeItem {
> +    PipeItem base;
> +    SpiceMsgMainName msg;
> +} NamePipeItem;
> +
> +typedef struct UuidPipeItem {
> +    PipeItem base;
> +    SpiceMsgMainUuid msg;
> +} UuidPipeItem;
> +
> +typedef struct NotifyPipeItem {
> +    PipeItem base;
> +    char *msg;
> +} NotifyPipeItem;
> +
> +typedef struct MultiMediaTimePipeItem {
> +    PipeItem base;
> +    int time;
> +} MultiMediaTimePipeItem;
> +
> +struct MainChannelClient {
> +    RedChannelClient base;
> +    uint32_t connection_id;
> +    uint32_t ping_id;
> +    uint32_t net_test_id;
> +    int net_test_stage;
> +    uint64_t latency;
> +    uint64_t bitrate_per_sec;
> +#ifdef RED_STATISTICS
> +    SpiceTimer *ping_timer;
> +    int ping_interval;
> +#endif
> +    int mig_wait_connect;
> +    int mig_connect_ok;
> +    int mig_wait_prev_complete;
> +    int mig_wait_prev_try_seamless;
> +    int init_sent;
> +    int seamless_mig_dst;
> +};
> +
> +enum NetTestStage {
> +    NET_TEST_STAGE_INVALID,
> +    NET_TEST_STAGE_WARMUP,
> +    NET_TEST_STAGE_LATENCY,
> +    NET_TEST_STAGE_RATE,
> +    NET_TEST_STAGE_COMPLETE,
> +};
> +
> +static void main_channel_release_pipe_item(RedChannelClient *rcc,
> +                                           PipeItem *base, int item_pushed);
> +
> +int main_channel_is_connected(MainChannel *main_chan)
> +{
> +    return red_channel_is_connected(&main_chan->base);
> +}
> +
> +/*
> + * When the main channel is disconnected, disconnect the entire client.
> + */
> +static void main_channel_client_on_disconnect(RedChannelClient *rcc)
> +{
> +    spice_printerr("rcc=%p", rcc);
> +    main_dispatcher_client_disconnect(rcc->client);
> +}
> +
> +RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t connection_id)
> +{
> +    RingItem *link;
> +    MainChannelClient *mcc;
> +
> +    RING_FOREACH(link, &main_chan->base.clients) {
> +        mcc = SPICE_CONTAINEROF(link, MainChannelClient, base.channel_link);
> +        if (mcc->connection_id == connection_id) {
> +            return mcc->base.client;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
> +
> +void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate)
> +{
> +    if (!mcc || mcc->net_test_id) {
> +        return;
> +    }
> +    if (test_rate) {
> +        if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
> +            && main_channel_client_push_ping(mcc, 0)
> +            && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
> +            mcc->net_test_id = mcc->ping_id - 2;
> +            mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
> +        }
> +    } else {
> +        red_channel_client_start_connectivity_monitoring(&mcc->base, CLIENT_CONNECTIVITY_TIMEOUT);
> +    }
> +}
> +
> +typedef struct MainMouseModeItemInfo {
> +    int current_mode;
> +    int is_client_mouse_allowed;
> +} MainMouseModeItemInfo;
> +
> +static PipeItem *main_mouse_mode_item_new(RedChannelClient *rcc, void *data, int num)
> +{
> +    MouseModePipeItem *item = spice_malloc(sizeof(MouseModePipeItem));
> +    MainMouseModeItemInfo *info = data;
> +
> +    red_channel_pipe_item_init(rcc->channel, &item->base,
> +                               PIPE_ITEM_TYPE_MAIN_MOUSE_MODE);
> +    item->current_mode = info->current_mode;
> +    item->is_client_mouse_allowed = info->is_client_mouse_allowed;
> +    return &item->base;
> +}
> +
> +static PipeItem *main_ping_item_new(MainChannelClient *mcc, int size)
> +{
> +    PingPipeItem *item = spice_malloc(sizeof(PingPipeItem));
> +
> +    red_channel_pipe_item_init(mcc->base.channel, &item->base, PIPE_ITEM_TYPE_MAIN_PING);
> +    item->size = size;
> +    return &item->base;
> +}
> +
> +static PipeItem *main_agent_tokens_item_new(RedChannelClient *rcc, uint32_t num_tokens)
> +{
> +    TokensPipeItem *item = spice_malloc(sizeof(TokensPipeItem));
> +
> +    red_channel_pipe_item_init(rcc->channel, &item->base,
> +                               PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN);
> +    item->tokens = num_tokens;
> +    return &item->base;
> +}
> +
> +static PipeItem *main_agent_data_item_new(RedChannelClient *rcc, uint8_t* data, size_t len,
> +                                          spice_marshaller_item_free_func free_data,
> +                                          void *opaque)
> +{
> +    AgentDataPipeItem *item = spice_malloc(sizeof(AgentDataPipeItem));
> +
> +    red_channel_pipe_item_init(rcc->channel, &item->base,
> +                               PIPE_ITEM_TYPE_MAIN_AGENT_DATA);
> +    item->data = data;
> +    item->len = len;
> +    item->free_data = free_data;
> +    item->opaque = opaque;
> +    return &item->base;
> +}
> +
> +static PipeItem *main_init_item_new(MainChannelClient *mcc,
> +    int connection_id, int display_channels_hint, int current_mouse_mode,
> +    int is_client_mouse_allowed, int multi_media_time,
> +    int ram_hint)
> +{
> +    InitPipeItem *item = spice_malloc(sizeof(InitPipeItem));
> +
> +    red_channel_pipe_item_init(mcc->base.channel, &item->base,
> +                               PIPE_ITEM_TYPE_MAIN_INIT);
> +    item->connection_id = connection_id;
> +    item->display_channels_hint = display_channels_hint;
> +    item->current_mouse_mode = current_mouse_mode;
> +    item->is_client_mouse_allowed = is_client_mouse_allowed;
> +    item->multi_media_time = multi_media_time;
> +    item->ram_hint = ram_hint;
> +    return &item->base;
> +}
> +
> +static PipeItem *main_name_item_new(MainChannelClient *mcc, const char *name)
> +{
> +    NamePipeItem *item = spice_malloc(sizeof(NamePipeItem) + strlen(name) + 1);
> +
> +    red_channel_pipe_item_init(mcc->base.channel, &item->base,
> +                               PIPE_ITEM_TYPE_MAIN_NAME);
> +    item->msg.name_len = strlen(name) + 1;
> +    memcpy(&item->msg.name, name, item->msg.name_len);
> +
> +    return &item->base;
> +}
> +
> +static PipeItem *main_uuid_item_new(MainChannelClient *mcc, const uint8_t uuid[16])
> +{
> +    UuidPipeItem *item = spice_malloc(sizeof(UuidPipeItem));
> +
> +    red_channel_pipe_item_init(mcc->base.channel, &item->base,
> +                               PIPE_ITEM_TYPE_MAIN_UUID);
> +    memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid));
> +
> +    return &item->base;
> +}
> +
> +static PipeItem *main_notify_item_new(RedChannelClient *rcc, void *data, int num)
> +{
> +    NotifyPipeItem *item = spice_malloc(sizeof(NotifyPipeItem));
> +    const char *msg = data;
> +
> +    red_channel_pipe_item_init(rcc->channel, &item->base,
> +                               PIPE_ITEM_TYPE_MAIN_NOTIFY);
> +    item->msg = spice_strdup(msg);
> +    return &item->base;
> +}
> +
> +static PipeItem *main_multi_media_time_item_new(
> +    RedChannelClient *rcc, void *data, int num)
> +{
> +    MultiMediaTimePipeItem *item, *info = data;
> +
> +    item = spice_malloc(sizeof(MultiMediaTimePipeItem));
> +    red_channel_pipe_item_init(rcc->channel, &item->base,
> +                               PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME);
> +    item->time = info->time;
> +    return &item->base;
> +}
> +
> +static void main_channel_push_channels(MainChannelClient *mcc)
> +{
> +    if (red_client_during_migrate_at_target(mcc->base.client)) {
> +        spice_printerr("warning: ignoring unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS"
> +                   "during migration");
> +        return;
> +    }
> +    red_channel_client_pipe_add_type(&mcc->base, PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
> +}
> +
> +static void main_channel_marshall_channels(RedChannelClient *rcc,
> +                                           SpiceMarshaller *m,
> +                                           PipeItem *item)
> +{
> +    SpiceMsgChannels* channels_info;
> +
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_CHANNELS_LIST, item);
> +    channels_info = (SpiceMsgChannels *)spice_malloc(sizeof(SpiceMsgChannels)
> +                            + reds_num_of_channels() * sizeof(SpiceChannelId));
> +    reds_fill_channels(channels_info);
> +    spice_marshall_msg_main_channels_list(m, channels_info);
> +    free(channels_info);
> +}
> +
> +int main_channel_client_push_ping(MainChannelClient *mcc, int size)
> +{
> +    PipeItem *item;
> +
> +    if (mcc == NULL) {
> +        return FALSE;
> +    }
> +    item = main_ping_item_new(mcc, size);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +    return TRUE;
> +}
> +
> +static void main_channel_marshall_ping(RedChannelClient *rcc,
> +                                       SpiceMarshaller *m,
> +                                       PingPipeItem *item)
> +{
> +    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> +    struct timespec time_space;
> +    SpiceMsgPing ping;
> +    int size_left = item->size;
> +
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_PING, &item->base);
> +    ping.id = ++(mcc->ping_id);
> +    clock_gettime(CLOCK_MONOTONIC, &time_space);
> +    ping.timestamp = time_space.tv_sec * 1000000LL + time_space.tv_nsec / 1000LL;
> +    spice_marshall_msg_ping(m, &ping);
> +
> +    while (size_left > 0) {
> +        int now = MIN(ZERO_BUF_SIZE, size_left);
> +        size_left -= now;
> +        spice_marshaller_add_ref(m, zero_page, now);
> +    }
> +}
> +
> +void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode,
> +                                  int is_client_mouse_allowed)
> +{
> +    MainMouseModeItemInfo info = {
> +        .current_mode=current_mode,
> +        .is_client_mouse_allowed=is_client_mouse_allowed,
> +    };
> +
> +    red_channel_pipes_new_add_push(&main_chan->base,
> +        main_mouse_mode_item_new, &info);
> +}
> +
> +static void main_channel_marshall_mouse_mode(RedChannelClient *rcc,
> +                                             SpiceMarshaller *m,
> +                                             MouseModePipeItem *item)
> +{
> +    SpiceMsgMainMouseMode mouse_mode;
> +
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MOUSE_MODE, &item->base);
> +    mouse_mode.supported_modes = SPICE_MOUSE_MODE_SERVER;
> +    if (item->is_client_mouse_allowed) {
> +        mouse_mode.supported_modes |= SPICE_MOUSE_MODE_CLIENT;
> +    }
> +    mouse_mode.current_mode = item->current_mode;
> +    spice_marshall_msg_main_mouse_mode(m, &mouse_mode);
> +}
> +
> +void main_channel_push_agent_connected(MainChannel *main_chan)
> +{
> +    if (red_channel_test_remote_cap(&main_chan->base, SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)) {
> +        red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS);
> +    } else {
> +        red_channel_pipes_add_empty_msg(&main_chan->base, SPICE_MSG_MAIN_AGENT_CONNECTED);
> +    }
> +}
> +
> +static void main_channel_marshall_agent_connected(SpiceMarshaller *m,
> +                                                  RedChannelClient *rcc,
> +                                                  PipeItem *item)
> +{
> +    SpiceMsgMainAgentConnectedTokens connected;
> +
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS, item);
> +    connected.num_tokens = REDS_AGENT_WINDOW_SIZE;
> +    spice_marshall_msg_main_agent_connected_tokens(m, &connected);
> +}
> +
> +void main_channel_push_agent_disconnected(MainChannel *main_chan)
> +{
> +    red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED);
> +}
> +
> +static void main_channel_marshall_agent_disconnected(RedChannelClient *rcc,
> +                                                     SpiceMarshaller *m,
> +                                                     PipeItem *item)
> +{
> +    SpiceMsgMainAgentDisconnect disconnect;
> +
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_DISCONNECTED, item);
> +    disconnect.error_code = SPICE_LINK_ERR_OK;
> +    spice_marshall_msg_main_agent_disconnected(m, &disconnect);
> +}
> +
> +void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens)
> +{
> +    PipeItem *item = main_agent_tokens_item_new(&mcc->base, num_tokens);
> +
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +static void main_channel_marshall_tokens(RedChannelClient *rcc,
> +                                         SpiceMarshaller *m, TokensPipeItem *item)
> +{
> +    SpiceMsgMainAgentTokens tokens;
> +
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_TOKEN, &item->base);
> +    tokens.num_tokens = item->tokens;
> +    spice_marshall_msg_main_agent_token(m, &tokens);
> +}
> +
> +void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
> +           spice_marshaller_item_free_func free_data, void *opaque)
> +{
> +    PipeItem *item;
> +
> +    item = main_agent_data_item_new(&mcc->base, data, len, free_data, opaque);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +static void main_channel_marshall_agent_data(RedChannelClient *rcc,
> +                                             SpiceMarshaller *m,
> +                                             AgentDataPipeItem *item)
> +{
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_DATA, &item->base);
> +    spice_marshaller_add_ref(m, item->data, item->len);
> +}
> +
> +static void main_channel_push_migrate_data_item(MainChannel *main_chan)
> +{
> +    red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA);
> +}
> +
> +static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc,
> +                                                    SpiceMarshaller *m, PipeItem *item)
> +{
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item);
> +    reds_marshall_migrate_data(m); // TODO: from reds split. ugly separation.
> +}
> +
> +static int main_channel_handle_migrate_data(RedChannelClient *rcc,
> +    uint32_t size, void *message)
> +{
> +    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> +    SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message;
> +
> +    /* not supported with multi-clients */
> +    spice_assert(rcc->channel->clients_num == 1);
> +
> +    if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataMain)) {
> +        spice_printerr("bad message size %u", size);
> +        return FALSE;
> +    }
> +    if (!migration_protocol_validate_header(header,
> +                                            SPICE_MIGRATE_DATA_MAIN_MAGIC,
> +                                            SPICE_MIGRATE_DATA_MAIN_VERSION)) {
> +        spice_error("bad header");
> +        return FALSE;
> +    }
> +    return reds_handle_migrate_data(mcc, (SpiceMigrateDataMain *)(header + 1), size);
> +}
> +
> +void main_channel_push_init(MainChannelClient *mcc,
> +    int display_channels_hint, int current_mouse_mode,
> +    int is_client_mouse_allowed, int multi_media_time,
> +    int ram_hint)
> +{
> +    PipeItem *item;
> +
> +    item = main_init_item_new(mcc,
> +             mcc->connection_id, display_channels_hint, current_mouse_mode,
> +             is_client_mouse_allowed, multi_media_time, ram_hint);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +static void main_channel_marshall_init(RedChannelClient *rcc,
> +                                       SpiceMarshaller *m,
> +                                       InitPipeItem *item)
> +{
> +    SpiceMsgMainInit init; // TODO - remove this copy, make InitPipeItem reuse SpiceMsgMainInit
> +
> +
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_INIT, &item->base);
> +    init.session_id = item->connection_id;
> +    init.display_channels_hint = item->display_channels_hint;
> +    init.current_mouse_mode = item->current_mouse_mode;
> +    init.supported_mouse_modes = SPICE_MOUSE_MODE_SERVER;
> +    if (item->is_client_mouse_allowed) {
> +        init.supported_mouse_modes |= SPICE_MOUSE_MODE_CLIENT;
> +    }
> +    init.agent_connected = reds_has_vdagent();
> +    init.agent_tokens = REDS_AGENT_WINDOW_SIZE;
> +    init.multi_media_time = item->multi_media_time;
> +    init.ram_hint = item->ram_hint;
> +    spice_marshall_msg_main_init(m, &init);
> +}
> +
> +void main_channel_push_name(MainChannelClient *mcc, const char *name)
> +{
> +    PipeItem *item;
> +
> +    if (!red_channel_client_test_remote_cap(&mcc->base,
> +                                            SPICE_MAIN_CAP_NAME_AND_UUID))
> +        return;
> +
> +    item = main_name_item_new(mcc, name);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +void main_channel_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16])
> +{
> +    PipeItem *item;
> +
> +    if (!red_channel_client_test_remote_cap(&mcc->base,
> +                                            SPICE_MAIN_CAP_NAME_AND_UUID))
> +        return;
> +
> +    item = main_uuid_item_new(mcc, uuid);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg)
> +{
> +    PipeItem *item = main_notify_item_new(&mcc->base, (void *)msg, 1);
> +    red_channel_client_pipe_add_push(&mcc->base, item);
> +}
> +
> +static void main_channel_marshall_notify(RedChannelClient *rcc,
> +                                         SpiceMarshaller *m, NotifyPipeItem *item)
> +{
> +    SpiceMsgNotify notify;
> +
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_NOTIFY, &item->base);
> +    notify.time_stamp = red_get_monotonic_time(); // TODO - move to main_new_notify_item
> +    notify.severity = SPICE_NOTIFY_SEVERITY_WARN;
> +    notify.visibilty = SPICE_NOTIFY_VISIBILITY_HIGH;
> +    notify.what = SPICE_WARN_GENERAL;
> +    notify.message_len = strlen(item->msg);
> +    spice_marshall_msg_notify(m, &notify);
> +    spice_marshaller_add(m, (uint8_t *)item->msg, notify.message_len + 1);
> +}
> +
> +static void main_channel_fill_migrate_dst_info(MainChannel *main_channel,
> +                                               SpiceMigrationDstInfo *dst_info)
> +{
> +    RedsMigSpice *mig_dst = &main_channel->mig_target;
> +    dst_info->port = mig_dst->port;
> +    dst_info->sport = mig_dst->sport;
> +    dst_info->host_size = strlen(mig_dst->host) + 1;
> +    dst_info->host_data = (uint8_t *)mig_dst->host;
> +    if (mig_dst->cert_subject) {
> +        dst_info->cert_subject_size = strlen(mig_dst->cert_subject) + 1;
> +        dst_info->cert_subject_data = (uint8_t *)mig_dst->cert_subject;
> +    } else {
> +        dst_info->cert_subject_size = 0;
> +        dst_info->cert_subject_data = NULL;
> +    }
> +}
> +
> +static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, RedChannelClient *rcc,
> +                                                PipeItem *item)
> +{
> +    SpiceMsgMainMigrationBegin migrate;
> +    MainChannel *main_ch;
> +
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN, item);
> +    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
> +    main_channel_fill_migrate_dst_info(main_ch, &migrate.dst_info);
> +    spice_marshall_msg_main_migrate_begin(m, &migrate);
> +}
> +
> +static void main_channel_marshall_migrate_begin_seamless(SpiceMarshaller *m,
> +                                                         RedChannelClient *rcc,
> +                                                         PipeItem *item)
> +{
> +    SpiceMsgMainMigrateBeginSeamless migrate_seamless;
> +    MainChannel *main_ch;
> +
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS, item);
> +    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
> +    main_channel_fill_migrate_dst_info(main_ch, &migrate_seamless.dst_info);
> +    migrate_seamless.src_mig_version = SPICE_MIGRATION_PROTOCOL_VERSION;
> +    spice_marshall_msg_main_migrate_begin_seamless(m, &migrate_seamless);
> +}
> +
> +void main_channel_push_multi_media_time(MainChannel *main_chan, int time)
> +{
> +    MultiMediaTimePipeItem info = {
> +        .time = time,
> +    };
> +
> +    red_channel_pipes_new_add_push(&main_chan->base,
> +        main_multi_media_time_item_new, &info);
> +}
> +
> +static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice *mig_target)
> +{
> +    spice_assert(mig_target);
> +    free(main_channel->mig_target.host);
> +    main_channel->mig_target.host = spice_strdup(mig_target->host);
> +    free(main_channel->mig_target.cert_subject);
> +    if (mig_target->cert_subject) {
> +        main_channel->mig_target.cert_subject = spice_strdup(mig_target->cert_subject);
> +    } else {
> +        main_channel->mig_target.cert_subject = NULL;
> +    }
> +    main_channel->mig_target.port = mig_target->port;
> +    main_channel->mig_target.sport = mig_target->sport;
> +}
> +
> +void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target)
> +{
> +    main_channel_fill_mig_target(main_chan, mig_target);
> +    red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
> +}
> +
> +static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelClient *rcc,
> +                                                 PipeItem *item)
> +{
> +    SpiceMsgMainMigrationSwitchHost migrate;
> +    MainChannel *main_ch;
> +
> +    spice_printerr("");
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST, item);
> +    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
> +    migrate.port = main_ch->mig_target.port;
> +    migrate.sport = main_ch->mig_target.sport;
> +    migrate.host_size = strlen(main_ch->mig_target.host) + 1;
> +    migrate.host_data = (uint8_t *)main_ch->mig_target.host;
> +    if (main_ch->mig_target.cert_subject) {
> +        migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1;
> +        migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject;
> +    } else {
> +        migrate.cert_subject_size = 0;
> +        migrate.cert_subject_data = NULL;
> +    }
> +    spice_marshall_msg_main_migrate_switch_host(m, &migrate);
> +}
> +
> +static void main_channel_marshall_multi_media_time(RedChannelClient *rcc,
> +                                                   SpiceMarshaller *m,
> +                                                   MultiMediaTimePipeItem *item)
> +{
> +    SpiceMsgMainMultiMediaTime time_mes;
> +
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MULTI_MEDIA_TIME, &item->base);
> +    time_mes.time = item->time;
> +    spice_marshall_msg_main_multi_media_time(m, &time_mes);
> +}
> +
> +static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
> +{
> +    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> +    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
> +
> +    /* In semi-seamless migration (dest side), the connection is started from scratch, and
> +     * we ignore any pipe item that arrives before the INIT msg is sent.
> +     * For seamless we don't send INIT, and the connection continues from the same place
> +     * it stopped on the src side. */
> +    if (!mcc->init_sent && !mcc->seamless_mig_dst && base->type != PIPE_ITEM_TYPE_MAIN_INIT) {
> +        spice_printerr("Init msg for client %p was not sent yet "
> +                       "(client is probably during semi-seamless migration). Ignoring msg type %d",
> +                   rcc->client, base->type);
> +        main_channel_release_pipe_item(rcc, base, FALSE);
> +        return;
> +    }
> +    switch (base->type) {
> +        case PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST:
> +            main_channel_marshall_channels(rcc, m, base);
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_PING:
> +            main_channel_marshall_ping(rcc, m,
> +                SPICE_CONTAINEROF(base, PingPipeItem, base));
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_MOUSE_MODE:
> +            {
> +                MouseModePipeItem *item =
> +                    SPICE_CONTAINEROF(base, MouseModePipeItem, base);
> +                main_channel_marshall_mouse_mode(rcc, m, item);
> +                break;
> +            }
> +        case PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED:
> +            main_channel_marshall_agent_disconnected(rcc, m, base);
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN:
> +            main_channel_marshall_tokens(rcc, m,
> +                SPICE_CONTAINEROF(base, TokensPipeItem, base));
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_AGENT_DATA:
> +            main_channel_marshall_agent_data(rcc, m,
> +                SPICE_CONTAINEROF(base, AgentDataPipeItem, base));
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA:
> +            main_channel_marshall_migrate_data_item(rcc, m, base);
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_INIT:
> +            mcc->init_sent = TRUE;
> +            main_channel_marshall_init(rcc, m,
> +                SPICE_CONTAINEROF(base, InitPipeItem, base));
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_NOTIFY:
> +            main_channel_marshall_notify(rcc, m,
> +                SPICE_CONTAINEROF(base, NotifyPipeItem, base));
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN:
> +            main_channel_marshall_migrate_begin(m, rcc, base);
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS:
> +            main_channel_marshall_migrate_begin_seamless(m, rcc, base);
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME:
> +            main_channel_marshall_multi_media_time(rcc, m,
> +                SPICE_CONTAINEROF(base, MultiMediaTimePipeItem, base));
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST:
> +            main_channel_marshall_migrate_switch(m, rcc, base);
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_NAME:
> +            red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_NAME, base);
> +            spice_marshall_msg_main_name(m, &SPICE_CONTAINEROF(base, NamePipeItem, base)->msg);
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_UUID:
> +            red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_UUID, base);
> +            spice_marshall_msg_main_uuid(m, &SPICE_CONTAINEROF(base, UuidPipeItem, base)->msg);
> +            break;
> +        case PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS:
> +            main_channel_marshall_agent_connected(m, rcc, base);
> +            break;
> +        default:
> +            break;
> +    };
> +    red_channel_client_begin_send_message(rcc);
> +}
> +
> +static void main_channel_release_pipe_item(RedChannelClient *rcc,
> +    PipeItem *base, int item_pushed)
> +{
> +    switch (base->type) {
> +        case PIPE_ITEM_TYPE_MAIN_AGENT_DATA: {
> +                AgentDataPipeItem *data = (AgentDataPipeItem *)base;
> +
> +                data->free_data(data->data, data->opaque);
> +                break;
> +        }
> +        case PIPE_ITEM_TYPE_MAIN_NOTIFY: {
> +                NotifyPipeItem *data = (NotifyPipeItem *)base;
> +                free(data->msg);
> +                break;
> +        }
> +        default:
> +            break;
> +    }
> +    free(base);
> +}
> +
> +static void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
> +                                                         int success,
> +                                                         int seamless)
> +{
> +    spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless);
> +    if (mcc->mig_wait_connect) {
> +        MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base);
> +
> +        mcc->mig_wait_connect = FALSE;
> +        mcc->mig_connect_ok = success;
> +        spice_assert(main_channel->num_clients_mig_wait);
> +        spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
> +        if (!--main_channel->num_clients_mig_wait) {
> +            reds_on_main_migrate_connected(seamless && success);
> +        }
> +    } else {
> +        if (success) {
> +            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
> +            red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
> +        }
> +    }
> +}
> +
> +void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc,
> +                                                        uint32_t src_version)
> +{
> +    if (reds_on_migrate_dst_set_seamless(mcc, src_version)) {
> +        mcc->seamless_mig_dst = TRUE;
> +        red_channel_client_pipe_add_empty_msg(&mcc->base,
> +                                             SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK);
> +    } else {
> +        red_channel_client_pipe_add_empty_msg(&mcc->base,
> +                                              SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK);
> +    }
> +}
> +
> +void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
> +{
> +    if (!red_client_during_migrate_at_target(mcc->base.client)) {
> +        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END");
> +        return;
> +    }
> +    if (!red_channel_client_test_remote_cap(&mcc->base,
> +                                            SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
> +        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, "
> +                   "client does not support semi-seamless migration");
> +            return;
> +    }
> +    red_client_semi_seamless_migrate_complete(mcc->base.client);
> +}
> +
> +void main_channel_migrate_dst_complete(MainChannelClient *mcc)
> +{
> +    if (mcc->mig_wait_prev_complete) {
> +        if (mcc->mig_wait_prev_try_seamless) {
> +            spice_assert(mcc->base.channel->clients_num == 1);
> +            red_channel_client_pipe_add_type(&mcc->base,
> +                                             PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
> +        } else {
> +            red_channel_client_pipe_add_type(&mcc->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
> +        }
> +        mcc->mig_wait_connect = TRUE;
> +        mcc->mig_wait_prev_complete = FALSE;
> +    }
> +}
> +
> +static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type,
> +                                      void *message)
> +{
> +    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
> +    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> +
> +    switch (type) {
> +    case SPICE_MSGC_MAIN_AGENT_START: {
> +        SpiceMsgcMainAgentStart *tokens;
> +
> +        spice_printerr("agent start");
> +        if (!main_chan) {
> +            return FALSE;
> +        }
> +        tokens = (SpiceMsgcMainAgentStart *)message;
> +        reds_on_main_agent_start(mcc, tokens->num_tokens);
> +        break;
> +    }
> +    case SPICE_MSGC_MAIN_AGENT_DATA: {
> +        reds_on_main_agent_data(mcc, message, size);
> +        break;
> +    }
> +    case SPICE_MSGC_MAIN_AGENT_TOKEN: {
> +        SpiceMsgcMainAgentTokens *tokens;
> +
> +        tokens = (SpiceMsgcMainAgentTokens *)message;
> +        reds_on_main_agent_tokens(mcc, tokens->num_tokens);
> +        break;
> +    }
> +    case SPICE_MSGC_MAIN_ATTACH_CHANNELS:
> +        main_channel_push_channels(mcc);
> +        break;
> +    case SPICE_MSGC_MAIN_MIGRATE_CONNECTED:
> +        main_channel_client_handle_migrate_connected(mcc,
> +                                                     TRUE /* success */,
> +                                                     FALSE /* seamless */);
> +        break;
> +    case SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS:
> +        main_channel_client_handle_migrate_connected(mcc,
> +                                                     TRUE /* success */,
> +                                                     TRUE /* seamless */);
> +        break;
> +    case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:
> +        main_channel_client_handle_migrate_connected(mcc, FALSE, FALSE);
> +        break;
> +    case SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS:
> +        main_channel_client_handle_migrate_dst_do_seamless(mcc,
> +            ((SpiceMsgcMainMigrateDstDoSeamless *)message)->src_version);
> +        break;
> +    case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST:
> +        reds_on_main_mouse_mode_request(message, size);
> +        break;
> +    case SPICE_MSGC_PONG: {
> +        SpiceMsgPing *ping = (SpiceMsgPing *)message;
> +        uint64_t roundtrip;
> +        struct timespec ts;
> +
> +        clock_gettime(CLOCK_MONOTONIC, &ts);
> +        roundtrip = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL - ping->timestamp;
> +
> +        if (ping->id == mcc->net_test_id) {
> +            switch (mcc->net_test_stage) {
> +            case NET_TEST_STAGE_WARMUP:
> +                mcc->net_test_id++;
> +                mcc->net_test_stage = NET_TEST_STAGE_LATENCY;
> +                mcc->latency = roundtrip;
> +                break;
> +            case NET_TEST_STAGE_LATENCY:
> +                mcc->net_test_id++;
> +                mcc->net_test_stage = NET_TEST_STAGE_RATE;
> +                mcc->latency = MIN(mcc->latency, roundtrip);
> +                break;
> +            case NET_TEST_STAGE_RATE:
> +                mcc->net_test_id = 0;
> +                if (roundtrip <= mcc->latency) {
> +                    // probably high load on client or server result with incorrect values
> +                    spice_printerr("net test: invalid values, latency %" PRIu64
> +                                   " roundtrip %" PRIu64 ". assuming high"
> +                                   " bandwidth", mcc->latency, roundtrip);
> +                    mcc->latency = 0;
> +                    mcc->net_test_stage = NET_TEST_STAGE_INVALID;
> +                    red_channel_client_start_connectivity_monitoring(&mcc->base,
> +                                                                     CLIENT_CONNECTIVITY_TIMEOUT);
> +                    break;
> +                }
> +                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000
> +                                        / (roundtrip - mcc->latency);
> +                mcc->net_test_stage = NET_TEST_STAGE_COMPLETE;
> +                spice_printerr("net test: latency %f ms, bitrate %"PRIu64" bps (%f Mbps)%s",
> +                           (double)mcc->latency / 1000,
> +                           mcc->bitrate_per_sec,
> +                           (double)mcc->bitrate_per_sec / 1024 / 1024,
> +                           main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : "");
> +                red_channel_client_start_connectivity_monitoring(&mcc->base,
> +                                                                 CLIENT_CONNECTIVITY_TIMEOUT);
> +                break;
> +            default:
> +                spice_printerr("invalid net test stage, ping id %d test id %d stage %d",
> +                           ping->id,
> +                           mcc->net_test_id,
> +                           mcc->net_test_stage);
> +                mcc->net_test_stage = NET_TEST_STAGE_INVALID;
> +            }
> +            break;
> +        } else {
> +            /*
> +             * channel client monitors the connectivity using ping-pong messages
> +             */
> +            red_channel_client_handle_message(rcc, size, type, message);
> +        }
> +#ifdef RED_STATISTICS
> +        reds_update_stat_value(roundtrip);
> +#endif
> +        break;
> +    }
> +    case SPICE_MSGC_DISCONNECTING:
> +        break;
> +    case SPICE_MSGC_MAIN_MIGRATE_END:
> +        main_channel_client_handle_migrate_end(mcc);
> +        break;
> +    default:
> +        return red_channel_client_handle_message(rcc, size, type, message);
> +    }
> +    return TRUE;
> +}
> +
> +static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
> +                                               uint16_t type,
> +                                               uint32_t size)
> +{
> +    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
> +    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> +
> +    if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
> +        return reds_get_agent_data_buffer(mcc, size);
> +    } else {
> +        return main_chan->recv_buf;
> +    }
> +}
> +
> +static void main_channel_release_msg_rcv_buf(RedChannelClient *rcc,
> +                                               uint16_t type,
> +                                               uint32_t size,
> +                                               uint8_t *msg)
> +{
> +    if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
> +        reds_release_agent_data_buffer(msg);
> +    }
> +}
> +
> +static int main_channel_config_socket(RedChannelClient *rcc)
> +{
> +    return TRUE;
> +}
> +
> +static void main_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
> +{
> +}
> +
> +static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
> +{
> +    spice_debug(NULL);
> +    main_channel_push_migrate_data_item(SPICE_CONTAINEROF(rcc->channel,
> +                                        MainChannel, base));
> +    return TRUE;
> +}
> +
> +#ifdef RED_STATISTICS
> +static void do_ping_client(MainChannelClient *mcc,
> +    const char *opt, int has_interval, int interval)
> +{
> +    spice_printerr("");
> +    if (!opt) {
> +        main_channel_client_push_ping(mcc, 0);
> +    } else if (!strcmp(opt, "on")) {
> +        if (has_interval && interval > 0) {
> +            mcc->ping_interval = interval * 1000;
> +        }
> +        core->timer_start(mcc->ping_timer, mcc->ping_interval);
> +    } else if (!strcmp(opt, "off")) {
> +        core->timer_cancel(mcc->ping_timer);
> +    } else {
> +        return;
> +    }
> +}
> +
> +static void ping_timer_cb(void *opaque)
> +{
> +    MainChannelClient *mcc = opaque;
> +
> +    if (!red_channel_client_is_connected(&mcc->base)) {
> +        spice_printerr("not connected to peer, ping off");
> +        core->timer_cancel(mcc->ping_timer);
> +        return;
> +    }
> +    do_ping_client(mcc, NULL, 0, 0);
> +    core->timer_start(mcc->ping_timer, mcc->ping_interval);
> +}
> +#endif /* RED_STATISTICS */
> +
> +static MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
> +                                                     RedsStream *stream, uint32_t connection_id,
> +                                                     int num_common_caps, uint32_t *common_caps,
> +                                                     int num_caps, uint32_t *caps)
> +{
> +    MainChannelClient *mcc = (MainChannelClient*)
> +                             red_channel_client_create(sizeof(MainChannelClient), &main_chan->base,
> +                                                       client, stream, FALSE, num_common_caps,
> +                                                       common_caps, num_caps, caps);
> +    spice_assert(mcc != NULL);
> +    mcc->connection_id = connection_id;
> +    mcc->bitrate_per_sec = ~0;
> +#ifdef RED_STATISTICS
> +    if (!(mcc->ping_timer = core->timer_add(ping_timer_cb, NULL))) {
> +        spice_error("ping timer create failed");
> +    }
> +    mcc->ping_interval = PING_INTERVAL;
> +#endif
> +    return mcc;
> +}
> +
> +MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client,
> +                                     RedsStream *stream, uint32_t connection_id, int migration,
> +                                     int num_common_caps, uint32_t *common_caps, int num_caps,
> +                                     uint32_t *caps)
> +{
> +    MainChannelClient *mcc;
> +
> +    spice_assert(channel);
> +
> +    // TODO - migration - I removed it from channel creation, now put it
> +    // into usage somewhere (not an issue until we return migration to it's
> +    // former glory)
> +    spice_printerr("add main channel client");
> +    mcc = main_channel_client_create(channel, client, stream, connection_id,
> +                                     num_common_caps, common_caps,
> +                                     num_caps, caps);
> +    return mcc;
> +}
> +
> +int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen)
> +{
> +    return main_chan ? getsockname(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
> +}
> +
> +int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen)
> +{
> +    return main_chan ? getpeername(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
> +}
> +
> +// TODO: ? shouldn't it disonnect all clients? or shutdown all main_channels?
> +void main_channel_close(MainChannel *main_chan)
> +{
> +    int socketfd;
> +
> +    if (main_chan && (socketfd = red_channel_get_first_socket(&main_chan->base)) != -1) {
> +        close(socketfd);
> +    }
> +}
> +
> +int main_channel_client_is_network_info_initialized(MainChannelClient *mcc)
> +{
> +    return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE;
> +}
> +
> +int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
> +{
> +    // TODO: configurable?
> +    return mcc->bitrate_per_sec < 10 * 1024 * 1024;
> +}
> +
> +uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
> +{
> +    return mcc->bitrate_per_sec;
> +}
> +
> +uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
> +{
> +    return mcc->latency / 1000;
> +}
> +
> +static void main_channel_client_migrate(RedChannelClient *rcc)
> +{
> +    reds_on_main_channel_migrate(SPICE_CONTAINEROF(rcc, MainChannelClient, base));
> +    red_channel_client_default_migrate(rcc);
> +}
> +
> +MainChannel* main_channel_init(void)
> +{
> +    RedChannel *channel;
> +    ChannelCbs channel_cbs = { NULL, };
> +    ClientCbs client_cbs = {NULL, };
> +
> +    channel_cbs.config_socket = main_channel_config_socket;
> +    channel_cbs.on_disconnect = main_channel_client_on_disconnect;
> +    channel_cbs.send_item = main_channel_send_item;
> +    channel_cbs.hold_item = main_channel_hold_pipe_item;
> +    channel_cbs.release_item = main_channel_release_pipe_item;
> +    channel_cbs.alloc_recv_buf = main_channel_alloc_msg_rcv_buf;
> +    channel_cbs.release_recv_buf = main_channel_release_msg_rcv_buf;
> +    channel_cbs.handle_migrate_flush_mark = main_channel_handle_migrate_flush_mark;
> +    channel_cbs.handle_migrate_data = main_channel_handle_migrate_data;
> +
> +    // TODO: set the migration flag of the channel
> +    channel = red_channel_create_parser(sizeof(MainChannel), core,
> +                                        SPICE_CHANNEL_MAIN, 0,
> +                                        FALSE, /* handle_acks */
> +                                        spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL),
> +                                        main_channel_handle_parsed,
> +                                        &channel_cbs,
> +                                        SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER);
> +    spice_assert(channel);
> +    red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
> +    red_channel_set_cap(channel, SPICE_MAIN_CAP_SEAMLESS_MIGRATE);
> +
> +    client_cbs.migrate = main_channel_client_migrate;
> +    red_channel_register_client_cbs(channel, &client_cbs);
> +
> +    return (MainChannel *)channel;
> +}
> +
> +RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
> +{
> +    spice_assert(mcc);
> +    return &mcc->base;
> +}
> +
> +static int main_channel_connect_semi_seamless(MainChannel *main_channel)
> +{
> +    RingItem *client_link;
> +
> +    RING_FOREACH(client_link, &main_channel->base.clients) {
> +        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
> +                                                    base.channel_link);
> +        if (red_channel_client_test_remote_cap(&mcc->base,
> +                                               SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
> +            if (red_client_during_migrate_at_target(mcc->base.client)) {
> +                spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
> +                mcc->mig_wait_prev_complete = TRUE;
> +                mcc->mig_wait_prev_try_seamless = FALSE;
> +            } else {
> +                red_channel_client_pipe_add_type(&mcc->base,
> +                                                 PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
> +                mcc->mig_wait_connect = TRUE;
> +            }
> +            mcc->mig_connect_ok = FALSE;
> +            main_channel->num_clients_mig_wait++;
> +        }
> +    }
> +    return main_channel->num_clients_mig_wait;
> +}
> +
> +static int main_channel_connect_seamless(MainChannel *main_channel)
> +{
> +    RingItem *client_link;
> +
> +    spice_assert(main_channel->base.clients_num == 1);
> +
> +    RING_FOREACH(client_link, &main_channel->base.clients) {
> +        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
> +                                                    base.channel_link);
> +        spice_assert(red_channel_client_test_remote_cap(&mcc->base,
> +                                                        SPICE_MAIN_CAP_SEAMLESS_MIGRATE));
> +        if (red_client_during_migrate_at_target(mcc->base.client)) {
> +           spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
> +           mcc->mig_wait_prev_complete = TRUE;
> +           mcc->mig_wait_prev_try_seamless = TRUE;
> +        } else {
> +            red_channel_client_pipe_add_type(&mcc->base,
> +                                             PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
> +            mcc->mig_wait_connect = TRUE;
> +        }
> +        mcc->mig_connect_ok = FALSE;
> +        main_channel->num_clients_mig_wait++;
> +    }
> +    return main_channel->num_clients_mig_wait;
> +}
> +
> +int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target,
> +                                 int try_seamless)
> +{
> +    main_channel_fill_mig_target(main_channel, mig_target);
> +    main_channel->num_clients_mig_wait = 0;
> +
> +    if (!main_channel_is_connected(main_channel)) {
> +        return 0;
> +    }
> +
> +    if (!try_seamless) {
> +        return main_channel_connect_semi_seamless(main_channel);
> +    } else {
> +        RingItem *client_item;
> +        MainChannelClient *mcc;
> +
> +        client_item = ring_get_head(&main_channel->base.clients);
> +        mcc = SPICE_CONTAINEROF(client_item, MainChannelClient, base.channel_link);
> +
> +        if (!red_channel_client_test_remote_cap(&mcc->base,
> +                                                SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) {
> +            return main_channel_connect_semi_seamless(main_channel);
> +        } else {
> +            return main_channel_connect_seamless(main_channel);
> +        }
> +    }
> +
> +}
> +
> +void main_channel_migrate_cancel_wait(MainChannel *main_chan)
> +{
> +    RingItem *client_link;
> +
> +    RING_FOREACH(client_link, &main_chan->base.clients) {
> +        MainChannelClient *mcc;
> +
> +        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
> +        if (mcc->mig_wait_connect) {
> +            spice_printerr("client %p cancel wait connect", mcc->base.client);
> +            mcc->mig_wait_connect = FALSE;
> +            mcc->mig_connect_ok = FALSE;
> +        }
> +        mcc->mig_wait_prev_complete = FALSE;
> +    }
> +    main_chan->num_clients_mig_wait = 0;
> +}
> +
> +int main_channel_migrate_src_complete(MainChannel *main_chan, int success)
> +{
> +    RingItem *client_link;
> +    int semi_seamless_count = 0;
> +
> +    spice_printerr("");
> +
> +    if (ring_is_empty(&main_chan->base.clients)) {
> +        spice_printerr("no peer connected");
> +        return 0;
> +    }
> +
> +    RING_FOREACH(client_link, &main_chan->base.clients) {
> +        MainChannelClient *mcc;
> +        int semi_seamless_support;
> +
> +        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
> +        semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base,
> +                                                   SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
> +        if (semi_seamless_support && mcc->mig_connect_ok) {
> +            if (success) {
> +                spice_printerr("client %p MIGRATE_END", mcc->base.client);
> +                red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END);
> +                semi_seamless_count++;
> +            } else {
> +                spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
> +                red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
> +            }
> +        } else {
> +            if (success) {
> +                spice_printerr("client %p SWITCH_HOST", mcc->base.client);
> +                red_channel_client_pipe_add_type(&mcc->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
> +            }
> +        }
> +        mcc->mig_connect_ok = FALSE;
> +        mcc->mig_wait_connect = FALSE;
> +   }
> +   return semi_seamless_count;
> +}
> diff --git a/server/main-channel.h b/server/main-channel.h
> new file mode 100644
> index 0000000..9bd20f1
> --- /dev/null
> +++ b/server/main-channel.h
> @@ -0,0 +1,103 @@
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +
> +#ifndef __MAIN_CHANNEL_H__
> +#define __MAIN_CHANNEL_H__
> +
> +#include <stdint.h>
> +#include <spice/vd_agent.h>
> +#include "common/marshaller.h"
> +#include "red_channel.h"
> +
> +// TODO: Defines used to calculate receive buffer size, and also by reds.c
> +// other options: is to make a reds_main_consts.h, to duplicate defines.
> +#define REDS_AGENT_WINDOW_SIZE 10
> +#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
> +
> +// approximate max receive message size for main channel
> +#define MAIN_CHANNEL_RECEIVE_BUF_SIZE \
> +    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
> +
> +struct RedsMigSpice {
> +    char *host;
> +    char *cert_subject;
> +    int port;
> +    int sport;
> +};
> +typedef struct RedsMigSpice RedsMigSpice;
> +
> +typedef struct MainChannel {
> +    RedChannel base;
> +    uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE];
> +    RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?)
> +    int num_clients_mig_wait;
> +} MainChannel;
> +
> +
> +MainChannel *main_channel_init(void);
> +RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id);
> +/* This is a 'clone' from the reds.h Channel.link callback to allow passing link_id */
> +MainChannelClient *main_channel_link(MainChannel *, RedClient *client,
> +     RedsStream *stream, uint32_t link_id, int migration, int num_common_caps,
> +     uint32_t *common_caps, int num_caps, uint32_t *caps);
> +void main_channel_close(MainChannel *main_chan); // not destroy, just socket close
> +void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int is_client_mouse_allowed);
> +void main_channel_push_agent_connected(MainChannel *main_chan);
> +void main_channel_push_agent_disconnected(MainChannel *main_chan);
> +void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
> +void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
> +                                         spice_marshaller_item_free_func free_data, void *opaque);
> +void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate);
> +// TODO: huge. Consider making a reds_* interface for these functions
> +// and calling from main.
> +void main_channel_push_init(MainChannelClient *mcc, int display_channels_hint,
> +    int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time,
> +    int ram_hint);
> +void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg);
> +void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
> +int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
> +int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
> +
> +/*
> + * return TRUE if network test had been completed successfully.
> + * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set to 0
> + */
> +int main_channel_client_is_network_info_initialized(MainChannelClient *mcc);
> +int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
> +uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
> +uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
> +
> +int main_channel_is_connected(MainChannel *main_chan);
> +RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
> +
> +/* switch host migration */
> +void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target);
> +
> +/* semi seamless migration */
> +
> +/* returns the number of clients that we are waiting for their connection.
> + * try_seamless = 'true' when the seamless-migration=on in qemu command line */
> +int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target,
> +                                 int try_seamless);
> +void main_channel_migrate_cancel_wait(MainChannel *main_chan);
> +/* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/
> +int main_channel_migrate_src_complete(MainChannel *main_chan, int success);
> +void main_channel_migrate_dst_complete(MainChannelClient *mcc);
> +void main_channel_push_name(MainChannelClient *mcc, const char *name);
> +void main_channel_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]);
> +
> +#endif
> diff --git a/server/main-dispatcher.c b/server/main-dispatcher.c
> new file mode 100644
> index 0000000..eb7cee6
> --- /dev/null
> +++ b/server/main-dispatcher.c
> @@ -0,0 +1,217 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#include <config.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <pthread.h>
> +
> +#include "red_common.h"
> +#include "dispatcher.h"
> +#include "main-dispatcher.h"
> +#include "red_channel.h"
> +#include "reds.h"
> +
> +/*
> + * Main Dispatcher
> + * ===============
> + *
> + * Communication channel between any non main thread and the main thread.
> + *
> + * The main thread is that from which spice_server_init is called.
> + *
> + * Messages are single sized, sent from the non-main thread to the main-thread.
> + * No acknowledge is sent back. This prevents a possible deadlock with the main
> + * thread already waiting on a response for the existing red_dispatcher used
> + * by the worker thread.
> + *
> + * All events have three functions:
> + * main_dispatcher_<event_name> - non static, public function
> + * main_dispatcher_self_<event_name> - handler for main thread
> + * main_dispatcher_handle_<event_name> - handler for callback from main thread
> + *   seperate from self because it may send an ack or do other work in the future.
> + */
> +
> +typedef struct {
> +    Dispatcher base;
> +    SpiceCoreInterface *core;
> +} MainDispatcher;
> +
> +MainDispatcher main_dispatcher;
> +
> +enum {
> +    MAIN_DISPATCHER_CHANNEL_EVENT = 0,
> +    MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
> +    MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
> +    MAIN_DISPATCHER_CLIENT_DISCONNECT,
> +
> +    MAIN_DISPATCHER_NUM_MESSAGES
> +};
> +
> +typedef struct MainDispatcherChannelEventMessage {
> +    int event;
> +    SpiceChannelEventInfo *info;
> +} MainDispatcherChannelEventMessage;
> +
> +typedef struct MainDispatcherMigrateSeamlessDstCompleteMessage {
> +    RedClient *client;
> +} MainDispatcherMigrateSeamlessDstCompleteMessage;
> +
> +typedef struct MainDispatcherMmTimeLatencyMessage {
> +    RedClient *client;
> +    uint32_t latency;
> +} MainDispatcherMmTimeLatencyMessage;
> +
> +typedef struct MainDispatcherClientDisconnectMessage {
> +    RedClient *client;
> +} MainDispatcherClientDisconnectMessage;
> +
> +/* channel_event - calls core->channel_event, must be done in main thread */
> +static void main_dispatcher_self_handle_channel_event(
> +                                                int event,
> +                                                SpiceChannelEventInfo *info)
> +{
> +    reds_handle_channel_event(event, info);
> +}
> +
> +static void main_dispatcher_handle_channel_event(void *opaque,
> +                                                 void *payload)
> +{
> +    MainDispatcherChannelEventMessage *channel_event = payload;
> +
> +    main_dispatcher_self_handle_channel_event(channel_event->event,
> +                                              channel_event->info);
> +}
> +
> +void main_dispatcher_channel_event(int event, SpiceChannelEventInfo *info)
> +{
> +    MainDispatcherChannelEventMessage msg = {0,};
> +
> +    if (pthread_self() == main_dispatcher.base.self) {
> +        main_dispatcher_self_handle_channel_event(event, info);
> +        return;
> +    }
> +    msg.event = event;
> +    msg.info = info;
> +    dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_CHANNEL_EVENT,
> +                            &msg);
> +}
> +
> +
> +static void main_dispatcher_handle_migrate_complete(void *opaque,
> +                                                    void *payload)
> +{
> +    MainDispatcherMigrateSeamlessDstCompleteMessage *mig_complete = payload;
> +
> +    reds_on_client_seamless_migrate_complete(mig_complete->client);
> +    red_client_unref(mig_complete->client);
> +}
> +
> +static void main_dispatcher_handle_mm_time_latency(void *opaque,
> +                                                   void *payload)
> +{
> +    MainDispatcherMmTimeLatencyMessage *msg = payload;
> +    reds_set_client_mm_time_latency(msg->client, msg->latency);
> +    red_client_unref(msg->client);
> +}
> +
> +static void main_dispatcher_handle_client_disconnect(void *opaque,
> +                                                     void *payload)
> +{
> +    MainDispatcherClientDisconnectMessage *msg = payload;
> +
> +    spice_debug("client=%p", msg->client);
> +    reds_client_disconnect(msg->client);
> +    red_client_unref(msg->client);
> +}
> +
> +void main_dispatcher_seamless_migrate_dst_complete(RedClient *client)
> +{
> +    MainDispatcherMigrateSeamlessDstCompleteMessage msg;
> +
> +    if (pthread_self() == main_dispatcher.base.self) {
> +        reds_on_client_seamless_migrate_complete(client);
> +        return;
> +    }
> +
> +    msg.client = red_client_ref(client);
> +    dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
> +                            &msg);
> +}
> +
> +void main_dispatcher_set_mm_time_latency(RedClient *client, uint32_t latency)
> +{
> +    MainDispatcherMmTimeLatencyMessage msg;
> +
> +    if (pthread_self() == main_dispatcher.base.self) {
> +        reds_set_client_mm_time_latency(client, latency);
> +        return;
> +    }
> +
> +    msg.client = red_client_ref(client);
> +    msg.latency = latency;
> +    dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
> +                            &msg);
> +}
> +
> +void main_dispatcher_client_disconnect(RedClient *client)
> +{
> +    MainDispatcherClientDisconnectMessage msg;
> +
> +    if (!client->disconnecting) {
> +        spice_debug("client %p", client);
> +        msg.client = red_client_ref(client);
> +        dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_CLIENT_DISCONNECT,
> +                                &msg);
> +    } else {
> +        spice_debug("client %p already during disconnection", client);
> +    }
> +}
> +
> +static void dispatcher_handle_read(int fd, int event, void *opaque)
> +{
> +    Dispatcher *dispatcher = opaque;
> +
> +    dispatcher_handle_recv_read(dispatcher);
> +}
> +
> +/*
> + * FIXME:
> + * Reds routines shouldn't be exposed. Instead reds.c should register the callbacks,
> + * and the corresponding operations should be made only via main_dispatcher.
> + */
> +void main_dispatcher_init(SpiceCoreInterface *core)
> +{
> +    memset(&main_dispatcher, 0, sizeof(main_dispatcher));
> +    main_dispatcher.core = core;
> +    dispatcher_init(&main_dispatcher.base, MAIN_DISPATCHER_NUM_MESSAGES, &main_dispatcher.base);
> +    core->watch_add(main_dispatcher.base.recv_fd, SPICE_WATCH_EVENT_READ,
> +                    dispatcher_handle_read, &main_dispatcher.base);
> +    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_CHANNEL_EVENT,
> +                                main_dispatcher_handle_channel_event,
> +                                sizeof(MainDispatcherChannelEventMessage), 0 /* no ack */);
> +    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
> +                                main_dispatcher_handle_migrate_complete,
> +                                sizeof(MainDispatcherMigrateSeamlessDstCompleteMessage), 0 /* no ack */);
> +    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
> +                                main_dispatcher_handle_mm_time_latency,
> +                                sizeof(MainDispatcherMmTimeLatencyMessage), 0 /* no ack */);
> +    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_CLIENT_DISCONNECT,
> +                                main_dispatcher_handle_client_disconnect,
> +                                sizeof(MainDispatcherClientDisconnectMessage), 0 /* no ack */);
> +}
> diff --git a/server/main-dispatcher.h b/server/main-dispatcher.h
> new file mode 100644
> index 0000000..af40093
> --- /dev/null
> +++ b/server/main-dispatcher.h
> @@ -0,0 +1,36 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009-2015 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifndef MAIN_DISPATCHER_H
> +#define MAIN_DISPATCHER_H
> +
> +#include <spice.h>
> +#include "red_channel.h"
> +
> +void main_dispatcher_channel_event(int event, SpiceChannelEventInfo *info);
> +void main_dispatcher_seamless_migrate_dst_complete(RedClient *client);
> +void main_dispatcher_set_mm_time_latency(RedClient *client, uint32_t latency);
> +/*
> + * Disconnecting the client is always executed asynchronously,
> + * in order to protect from expired references in the routines
> + * that triggered the client destruction.
> + */
> +void main_dispatcher_client_disconnect(RedClient *client);
> +
> +void main_dispatcher_init(SpiceCoreInterface *core);
> +
> +#endif //MAIN_DISPATCHER_H
> diff --git a/server/main_channel.c b/server/main_channel.c
> deleted file mode 100644
> index 1af6baa..0000000
> --- a/server/main_channel.c
> +++ /dev/null
> @@ -1,1345 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifdef HAVE_CONFIG_H
> -#include <config.h>
> -#endif
> -
> -#include <inttypes.h>
> -#include <stdint.h>
> -#include <stdio.h>
> -#include <unistd.h>
> -#include <sys/socket.h>
> -#include <netinet/in.h>
> -#include <netinet/tcp.h>
> -#include <arpa/inet.h>
> -#include <netdb.h>
> -#include <limits.h>
> -#include <time.h>
> -#include <pthread.h>
> -#include <sys/mman.h>
> -#include <fcntl.h>
> -#include <errno.h>
> -#include <ctype.h>
> -
> -#include "common/generated_server_marshallers.h"
> -#include "common/messages.h"
> -#include "common/ring.h"
> -
> -#include "demarshallers.h"
> -#include "main_channel.h"
> -#include "red_channel.h"
> -#include "red_common.h"
> -#include "reds.h"
> -#include "migration_protocol.h"
> -#include "main_dispatcher.h"
> -#include "utils.h"
> -
> -#define ZERO_BUF_SIZE 4096
> -
> -#define NET_TEST_WARMUP_BYTES 0
> -#define NET_TEST_BYTES (1024 * 250)
> -
> -#define PING_INTERVAL (1000 * 10)
> -
> -#define CLIENT_CONNECTIVITY_TIMEOUT (30*1000) // 30 seconds
> -
> -static uint8_t zero_page[ZERO_BUF_SIZE] = {0};
> -
> -enum {
> -    PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = PIPE_ITEM_TYPE_CHANNEL_BASE,
> -    PIPE_ITEM_TYPE_MAIN_PING,
> -    PIPE_ITEM_TYPE_MAIN_MOUSE_MODE,
> -    PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED,
> -    PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN,
> -    PIPE_ITEM_TYPE_MAIN_AGENT_DATA,
> -    PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA,
> -    PIPE_ITEM_TYPE_MAIN_INIT,
> -    PIPE_ITEM_TYPE_MAIN_NOTIFY,
> -    PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN,
> -    PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS,
> -    PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST,
> -    PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME,
> -    PIPE_ITEM_TYPE_MAIN_NAME,
> -    PIPE_ITEM_TYPE_MAIN_UUID,
> -    PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
> -};
> -
> -typedef struct RefsPipeItem {
> -    PipeItem base;
> -    int *refs;
> -} RefsPipeItem;
> -
> -typedef struct PingPipeItem {
> -    PipeItem base;
> -    int size;
> -} PingPipeItem;
> -
> -typedef struct MouseModePipeItem {
> -    PipeItem base;
> -    int current_mode;
> -    int is_client_mouse_allowed;
> -} MouseModePipeItem;
> -
> -typedef struct TokensPipeItem {
> -    PipeItem base;
> -    int tokens;
> -} TokensPipeItem;
> -
> -typedef struct AgentDataPipeItem {
> -    PipeItem base;
> -    uint8_t* data;
> -    size_t len;
> -    spice_marshaller_item_free_func free_data;
> -    void *opaque;
> -} AgentDataPipeItem;
> -
> -typedef struct InitPipeItem {
> -    PipeItem base;
> -    int connection_id;
> -    int display_channels_hint;
> -    int current_mouse_mode;
> -    int is_client_mouse_allowed;
> -    int multi_media_time;
> -    int ram_hint;
> -} InitPipeItem;
> -
> -typedef struct NamePipeItem {
> -    PipeItem base;
> -    SpiceMsgMainName msg;
> -} NamePipeItem;
> -
> -typedef struct UuidPipeItem {
> -    PipeItem base;
> -    SpiceMsgMainUuid msg;
> -} UuidPipeItem;
> -
> -typedef struct NotifyPipeItem {
> -    PipeItem base;
> -    char *msg;
> -} NotifyPipeItem;
> -
> -typedef struct MultiMediaTimePipeItem {
> -    PipeItem base;
> -    int time;
> -} MultiMediaTimePipeItem;
> -
> -struct MainChannelClient {
> -    RedChannelClient base;
> -    uint32_t connection_id;
> -    uint32_t ping_id;
> -    uint32_t net_test_id;
> -    int net_test_stage;
> -    uint64_t latency;
> -    uint64_t bitrate_per_sec;
> -#ifdef RED_STATISTICS
> -    SpiceTimer *ping_timer;
> -    int ping_interval;
> -#endif
> -    int mig_wait_connect;
> -    int mig_connect_ok;
> -    int mig_wait_prev_complete;
> -    int mig_wait_prev_try_seamless;
> -    int init_sent;
> -    int seamless_mig_dst;
> -};
> -
> -enum NetTestStage {
> -    NET_TEST_STAGE_INVALID,
> -    NET_TEST_STAGE_WARMUP,
> -    NET_TEST_STAGE_LATENCY,
> -    NET_TEST_STAGE_RATE,
> -    NET_TEST_STAGE_COMPLETE,
> -};
> -
> -static void main_channel_release_pipe_item(RedChannelClient *rcc,
> -                                           PipeItem *base, int item_pushed);
> -
> -int main_channel_is_connected(MainChannel *main_chan)
> -{
> -    return red_channel_is_connected(&main_chan->base);
> -}
> -
> -/*
> - * When the main channel is disconnected, disconnect the entire client.
> - */
> -static void main_channel_client_on_disconnect(RedChannelClient *rcc)
> -{
> -    spice_printerr("rcc=%p", rcc);
> -    main_dispatcher_client_disconnect(rcc->client);
> -}
> -
> -RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t connection_id)
> -{
> -    RingItem *link;
> -    MainChannelClient *mcc;
> -
> -    RING_FOREACH(link, &main_chan->base.clients) {
> -        mcc = SPICE_CONTAINEROF(link, MainChannelClient, base.channel_link);
> -        if (mcc->connection_id == connection_id) {
> -            return mcc->base.client;
> -        }
> -    }
> -    return NULL;
> -}
> -
> -static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
> -
> -void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate)
> -{
> -    if (!mcc || mcc->net_test_id) {
> -        return;
> -    }
> -    if (test_rate) {
> -        if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
> -            && main_channel_client_push_ping(mcc, 0)
> -            && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
> -            mcc->net_test_id = mcc->ping_id - 2;
> -            mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
> -        }
> -    } else {
> -        red_channel_client_start_connectivity_monitoring(&mcc->base, CLIENT_CONNECTIVITY_TIMEOUT);
> -    }
> -}
> -
> -typedef struct MainMouseModeItemInfo {
> -    int current_mode;
> -    int is_client_mouse_allowed;
> -} MainMouseModeItemInfo;
> -
> -static PipeItem *main_mouse_mode_item_new(RedChannelClient *rcc, void *data, int num)
> -{
> -    MouseModePipeItem *item = spice_malloc(sizeof(MouseModePipeItem));
> -    MainMouseModeItemInfo *info = data;
> -
> -    red_channel_pipe_item_init(rcc->channel, &item->base,
> -                               PIPE_ITEM_TYPE_MAIN_MOUSE_MODE);
> -    item->current_mode = info->current_mode;
> -    item->is_client_mouse_allowed = info->is_client_mouse_allowed;
> -    return &item->base;
> -}
> -
> -static PipeItem *main_ping_item_new(MainChannelClient *mcc, int size)
> -{
> -    PingPipeItem *item = spice_malloc(sizeof(PingPipeItem));
> -
> -    red_channel_pipe_item_init(mcc->base.channel, &item->base, PIPE_ITEM_TYPE_MAIN_PING);
> -    item->size = size;
> -    return &item->base;
> -}
> -
> -static PipeItem *main_agent_tokens_item_new(RedChannelClient *rcc, uint32_t num_tokens)
> -{
> -    TokensPipeItem *item = spice_malloc(sizeof(TokensPipeItem));
> -
> -    red_channel_pipe_item_init(rcc->channel, &item->base,
> -                               PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN);
> -    item->tokens = num_tokens;
> -    return &item->base;
> -}
> -
> -static PipeItem *main_agent_data_item_new(RedChannelClient *rcc, uint8_t* data, size_t len,
> -                                          spice_marshaller_item_free_func free_data,
> -                                          void *opaque)
> -{
> -    AgentDataPipeItem *item = spice_malloc(sizeof(AgentDataPipeItem));
> -
> -    red_channel_pipe_item_init(rcc->channel, &item->base,
> -                               PIPE_ITEM_TYPE_MAIN_AGENT_DATA);
> -    item->data = data;
> -    item->len = len;
> -    item->free_data = free_data;
> -    item->opaque = opaque;
> -    return &item->base;
> -}
> -
> -static PipeItem *main_init_item_new(MainChannelClient *mcc,
> -    int connection_id, int display_channels_hint, int current_mouse_mode,
> -    int is_client_mouse_allowed, int multi_media_time,
> -    int ram_hint)
> -{
> -    InitPipeItem *item = spice_malloc(sizeof(InitPipeItem));
> -
> -    red_channel_pipe_item_init(mcc->base.channel, &item->base,
> -                               PIPE_ITEM_TYPE_MAIN_INIT);
> -    item->connection_id = connection_id;
> -    item->display_channels_hint = display_channels_hint;
> -    item->current_mouse_mode = current_mouse_mode;
> -    item->is_client_mouse_allowed = is_client_mouse_allowed;
> -    item->multi_media_time = multi_media_time;
> -    item->ram_hint = ram_hint;
> -    return &item->base;
> -}
> -
> -static PipeItem *main_name_item_new(MainChannelClient *mcc, const char *name)
> -{
> -    NamePipeItem *item = spice_malloc(sizeof(NamePipeItem) + strlen(name) + 1);
> -
> -    red_channel_pipe_item_init(mcc->base.channel, &item->base,
> -                               PIPE_ITEM_TYPE_MAIN_NAME);
> -    item->msg.name_len = strlen(name) + 1;
> -    memcpy(&item->msg.name, name, item->msg.name_len);
> -
> -    return &item->base;
> -}
> -
> -static PipeItem *main_uuid_item_new(MainChannelClient *mcc, const uint8_t uuid[16])
> -{
> -    UuidPipeItem *item = spice_malloc(sizeof(UuidPipeItem));
> -
> -    red_channel_pipe_item_init(mcc->base.channel, &item->base,
> -                               PIPE_ITEM_TYPE_MAIN_UUID);
> -    memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid));
> -
> -    return &item->base;
> -}
> -
> -static PipeItem *main_notify_item_new(RedChannelClient *rcc, void *data, int num)
> -{
> -    NotifyPipeItem *item = spice_malloc(sizeof(NotifyPipeItem));
> -    const char *msg = data;
> -
> -    red_channel_pipe_item_init(rcc->channel, &item->base,
> -                               PIPE_ITEM_TYPE_MAIN_NOTIFY);
> -    item->msg = spice_strdup(msg);
> -    return &item->base;
> -}
> -
> -static PipeItem *main_multi_media_time_item_new(
> -    RedChannelClient *rcc, void *data, int num)
> -{
> -    MultiMediaTimePipeItem *item, *info = data;
> -
> -    item = spice_malloc(sizeof(MultiMediaTimePipeItem));
> -    red_channel_pipe_item_init(rcc->channel, &item->base,
> -                               PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME);
> -    item->time = info->time;
> -    return &item->base;
> -}
> -
> -static void main_channel_push_channels(MainChannelClient *mcc)
> -{
> -    if (red_client_during_migrate_at_target(mcc->base.client)) {
> -        spice_printerr("warning: ignoring unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS"
> -                   "during migration");
> -        return;
> -    }
> -    red_channel_client_pipe_add_type(&mcc->base, PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
> -}
> -
> -static void main_channel_marshall_channels(RedChannelClient *rcc,
> -                                           SpiceMarshaller *m,
> -                                           PipeItem *item)
> -{
> -    SpiceMsgChannels* channels_info;
> -
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_CHANNELS_LIST, item);
> -    channels_info = (SpiceMsgChannels *)spice_malloc(sizeof(SpiceMsgChannels)
> -                            + reds_num_of_channels() * sizeof(SpiceChannelId));
> -    reds_fill_channels(channels_info);
> -    spice_marshall_msg_main_channels_list(m, channels_info);
> -    free(channels_info);
> -}
> -
> -int main_channel_client_push_ping(MainChannelClient *mcc, int size)
> -{
> -    PipeItem *item;
> -
> -    if (mcc == NULL) {
> -        return FALSE;
> -    }
> -    item = main_ping_item_new(mcc, size);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -    return TRUE;
> -}
> -
> -static void main_channel_marshall_ping(RedChannelClient *rcc,
> -                                       SpiceMarshaller *m,
> -                                       PingPipeItem *item)
> -{
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> -    struct timespec time_space;
> -    SpiceMsgPing ping;
> -    int size_left = item->size;
> -
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_PING, &item->base);
> -    ping.id = ++(mcc->ping_id);
> -    clock_gettime(CLOCK_MONOTONIC, &time_space);
> -    ping.timestamp = time_space.tv_sec * 1000000LL + time_space.tv_nsec / 1000LL;
> -    spice_marshall_msg_ping(m, &ping);
> -
> -    while (size_left > 0) {
> -        int now = MIN(ZERO_BUF_SIZE, size_left);
> -        size_left -= now;
> -        spice_marshaller_add_ref(m, zero_page, now);
> -    }
> -}
> -
> -void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode,
> -                                  int is_client_mouse_allowed)
> -{
> -    MainMouseModeItemInfo info = {
> -        .current_mode=current_mode,
> -        .is_client_mouse_allowed=is_client_mouse_allowed,
> -    };
> -
> -    red_channel_pipes_new_add_push(&main_chan->base,
> -        main_mouse_mode_item_new, &info);
> -}
> -
> -static void main_channel_marshall_mouse_mode(RedChannelClient *rcc,
> -                                             SpiceMarshaller *m,
> -                                             MouseModePipeItem *item)
> -{
> -    SpiceMsgMainMouseMode mouse_mode;
> -
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MOUSE_MODE, &item->base);
> -    mouse_mode.supported_modes = SPICE_MOUSE_MODE_SERVER;
> -    if (item->is_client_mouse_allowed) {
> -        mouse_mode.supported_modes |= SPICE_MOUSE_MODE_CLIENT;
> -    }
> -    mouse_mode.current_mode = item->current_mode;
> -    spice_marshall_msg_main_mouse_mode(m, &mouse_mode);
> -}
> -
> -void main_channel_push_agent_connected(MainChannel *main_chan)
> -{
> -    if (red_channel_test_remote_cap(&main_chan->base, SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)) {
> -        red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS);
> -    } else {
> -        red_channel_pipes_add_empty_msg(&main_chan->base, SPICE_MSG_MAIN_AGENT_CONNECTED);
> -    }
> -}
> -
> -static void main_channel_marshall_agent_connected(SpiceMarshaller *m,
> -                                                  RedChannelClient *rcc,
> -                                                  PipeItem *item)
> -{
> -    SpiceMsgMainAgentConnectedTokens connected;
> -
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS, item);
> -    connected.num_tokens = REDS_AGENT_WINDOW_SIZE;
> -    spice_marshall_msg_main_agent_connected_tokens(m, &connected);
> -}
> -
> -void main_channel_push_agent_disconnected(MainChannel *main_chan)
> -{
> -    red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED);
> -}
> -
> -static void main_channel_marshall_agent_disconnected(RedChannelClient *rcc,
> -                                                     SpiceMarshaller *m,
> -                                                     PipeItem *item)
> -{
> -    SpiceMsgMainAgentDisconnect disconnect;
> -
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_DISCONNECTED, item);
> -    disconnect.error_code = SPICE_LINK_ERR_OK;
> -    spice_marshall_msg_main_agent_disconnected(m, &disconnect);
> -}
> -
> -void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens)
> -{
> -    PipeItem *item = main_agent_tokens_item_new(&mcc->base, num_tokens);
> -
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
> -static void main_channel_marshall_tokens(RedChannelClient *rcc,
> -                                         SpiceMarshaller *m, TokensPipeItem *item)
> -{
> -    SpiceMsgMainAgentTokens tokens;
> -
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_TOKEN, &item->base);
> -    tokens.num_tokens = item->tokens;
> -    spice_marshall_msg_main_agent_token(m, &tokens);
> -}
> -
> -void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
> -           spice_marshaller_item_free_func free_data, void *opaque)
> -{
> -    PipeItem *item;
> -
> -    item = main_agent_data_item_new(&mcc->base, data, len, free_data, opaque);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
> -static void main_channel_marshall_agent_data(RedChannelClient *rcc,
> -                                             SpiceMarshaller *m,
> -                                             AgentDataPipeItem *item)
> -{
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_DATA, &item->base);
> -    spice_marshaller_add_ref(m, item->data, item->len);
> -}
> -
> -static void main_channel_push_migrate_data_item(MainChannel *main_chan)
> -{
> -    red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA);
> -}
> -
> -static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc,
> -                                                    SpiceMarshaller *m, PipeItem *item)
> -{
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item);
> -    reds_marshall_migrate_data(m); // TODO: from reds split. ugly separation.
> -}
> -
> -static int main_channel_handle_migrate_data(RedChannelClient *rcc,
> -    uint32_t size, void *message)
> -{
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> -    SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message;
> -
> -    /* not supported with multi-clients */
> -    spice_assert(rcc->channel->clients_num == 1);
> -
> -    if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataMain)) {
> -        spice_printerr("bad message size %u", size);
> -        return FALSE;
> -    }
> -    if (!migration_protocol_validate_header(header,
> -                                            SPICE_MIGRATE_DATA_MAIN_MAGIC,
> -                                            SPICE_MIGRATE_DATA_MAIN_VERSION)) {
> -        spice_error("bad header");
> -        return FALSE;
> -    }
> -    return reds_handle_migrate_data(mcc, (SpiceMigrateDataMain *)(header + 1), size);
> -}
> -
> -void main_channel_push_init(MainChannelClient *mcc,
> -    int display_channels_hint, int current_mouse_mode,
> -    int is_client_mouse_allowed, int multi_media_time,
> -    int ram_hint)
> -{
> -    PipeItem *item;
> -
> -    item = main_init_item_new(mcc,
> -             mcc->connection_id, display_channels_hint, current_mouse_mode,
> -             is_client_mouse_allowed, multi_media_time, ram_hint);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
> -static void main_channel_marshall_init(RedChannelClient *rcc,
> -                                       SpiceMarshaller *m,
> -                                       InitPipeItem *item)
> -{
> -    SpiceMsgMainInit init; // TODO - remove this copy, make InitPipeItem reuse SpiceMsgMainInit
> -
> -
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_INIT, &item->base);
> -    init.session_id = item->connection_id;
> -    init.display_channels_hint = item->display_channels_hint;
> -    init.current_mouse_mode = item->current_mouse_mode;
> -    init.supported_mouse_modes = SPICE_MOUSE_MODE_SERVER;
> -    if (item->is_client_mouse_allowed) {
> -        init.supported_mouse_modes |= SPICE_MOUSE_MODE_CLIENT;
> -    }
> -    init.agent_connected = reds_has_vdagent();
> -    init.agent_tokens = REDS_AGENT_WINDOW_SIZE;
> -    init.multi_media_time = item->multi_media_time;
> -    init.ram_hint = item->ram_hint;
> -    spice_marshall_msg_main_init(m, &init);
> -}
> -
> -void main_channel_push_name(MainChannelClient *mcc, const char *name)
> -{
> -    PipeItem *item;
> -
> -    if (!red_channel_client_test_remote_cap(&mcc->base,
> -                                            SPICE_MAIN_CAP_NAME_AND_UUID))
> -        return;
> -
> -    item = main_name_item_new(mcc, name);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
> -void main_channel_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16])
> -{
> -    PipeItem *item;
> -
> -    if (!red_channel_client_test_remote_cap(&mcc->base,
> -                                            SPICE_MAIN_CAP_NAME_AND_UUID))
> -        return;
> -
> -    item = main_uuid_item_new(mcc, uuid);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
> -void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg)
> -{
> -    PipeItem *item = main_notify_item_new(&mcc->base, (void *)msg, 1);
> -    red_channel_client_pipe_add_push(&mcc->base, item);
> -}
> -
> -static void main_channel_marshall_notify(RedChannelClient *rcc,
> -                                         SpiceMarshaller *m, NotifyPipeItem *item)
> -{
> -    SpiceMsgNotify notify;
> -
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_NOTIFY, &item->base);
> -    notify.time_stamp = red_get_monotonic_time(); // TODO - move to main_new_notify_item
> -    notify.severity = SPICE_NOTIFY_SEVERITY_WARN;
> -    notify.visibilty = SPICE_NOTIFY_VISIBILITY_HIGH;
> -    notify.what = SPICE_WARN_GENERAL;
> -    notify.message_len = strlen(item->msg);
> -    spice_marshall_msg_notify(m, &notify);
> -    spice_marshaller_add(m, (uint8_t *)item->msg, notify.message_len + 1);
> -}
> -
> -static void main_channel_fill_migrate_dst_info(MainChannel *main_channel,
> -                                               SpiceMigrationDstInfo *dst_info)
> -{
> -    RedsMigSpice *mig_dst = &main_channel->mig_target;
> -    dst_info->port = mig_dst->port;
> -    dst_info->sport = mig_dst->sport;
> -    dst_info->host_size = strlen(mig_dst->host) + 1;
> -    dst_info->host_data = (uint8_t *)mig_dst->host;
> -    if (mig_dst->cert_subject) {
> -        dst_info->cert_subject_size = strlen(mig_dst->cert_subject) + 1;
> -        dst_info->cert_subject_data = (uint8_t *)mig_dst->cert_subject;
> -    } else {
> -        dst_info->cert_subject_size = 0;
> -        dst_info->cert_subject_data = NULL;
> -    }
> -}
> -
> -static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, RedChannelClient *rcc,
> -                                                PipeItem *item)
> -{
> -    SpiceMsgMainMigrationBegin migrate;
> -    MainChannel *main_ch;
> -
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN, item);
> -    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
> -    main_channel_fill_migrate_dst_info(main_ch, &migrate.dst_info);
> -    spice_marshall_msg_main_migrate_begin(m, &migrate);
> -}
> -
> -static void main_channel_marshall_migrate_begin_seamless(SpiceMarshaller *m,
> -                                                         RedChannelClient *rcc,
> -                                                         PipeItem *item)
> -{
> -    SpiceMsgMainMigrateBeginSeamless migrate_seamless;
> -    MainChannel *main_ch;
> -
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS, item);
> -    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
> -    main_channel_fill_migrate_dst_info(main_ch, &migrate_seamless.dst_info);
> -    migrate_seamless.src_mig_version = SPICE_MIGRATION_PROTOCOL_VERSION;
> -    spice_marshall_msg_main_migrate_begin_seamless(m, &migrate_seamless);
> -}
> -
> -void main_channel_push_multi_media_time(MainChannel *main_chan, int time)
> -{
> -    MultiMediaTimePipeItem info = {
> -        .time = time,
> -    };
> -
> -    red_channel_pipes_new_add_push(&main_chan->base,
> -        main_multi_media_time_item_new, &info);
> -}
> -
> -static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice *mig_target)
> -{
> -    spice_assert(mig_target);
> -    free(main_channel->mig_target.host);
> -    main_channel->mig_target.host = spice_strdup(mig_target->host);
> -    free(main_channel->mig_target.cert_subject);
> -    if (mig_target->cert_subject) {
> -        main_channel->mig_target.cert_subject = spice_strdup(mig_target->cert_subject);
> -    } else {
> -        main_channel->mig_target.cert_subject = NULL;
> -    }
> -    main_channel->mig_target.port = mig_target->port;
> -    main_channel->mig_target.sport = mig_target->sport;
> -}
> -
> -void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target)
> -{
> -    main_channel_fill_mig_target(main_chan, mig_target);
> -    red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
> -}
> -
> -static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelClient *rcc,
> -                                                 PipeItem *item)
> -{
> -    SpiceMsgMainMigrationSwitchHost migrate;
> -    MainChannel *main_ch;
> -
> -    spice_printerr("");
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST, item);
> -    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
> -    migrate.port = main_ch->mig_target.port;
> -    migrate.sport = main_ch->mig_target.sport;
> -    migrate.host_size = strlen(main_ch->mig_target.host) + 1;
> -    migrate.host_data = (uint8_t *)main_ch->mig_target.host;
> -    if (main_ch->mig_target.cert_subject) {
> -        migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1;
> -        migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject;
> -    } else {
> -        migrate.cert_subject_size = 0;
> -        migrate.cert_subject_data = NULL;
> -    }
> -    spice_marshall_msg_main_migrate_switch_host(m, &migrate);
> -}
> -
> -static void main_channel_marshall_multi_media_time(RedChannelClient *rcc,
> -                                                   SpiceMarshaller *m,
> -                                                   MultiMediaTimePipeItem *item)
> -{
> -    SpiceMsgMainMultiMediaTime time_mes;
> -
> -    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MULTI_MEDIA_TIME, &item->base);
> -    time_mes.time = item->time;
> -    spice_marshall_msg_main_multi_media_time(m, &time_mes);
> -}
> -
> -static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
> -{
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> -    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
> -
> -    /* In semi-seamless migration (dest side), the connection is started from scratch, and
> -     * we ignore any pipe item that arrives before the INIT msg is sent.
> -     * For seamless we don't send INIT, and the connection continues from the same place
> -     * it stopped on the src side. */
> -    if (!mcc->init_sent && !mcc->seamless_mig_dst && base->type != PIPE_ITEM_TYPE_MAIN_INIT) {
> -        spice_printerr("Init msg for client %p was not sent yet "
> -                       "(client is probably during semi-seamless migration). Ignoring msg type %d",
> -                   rcc->client, base->type);
> -        main_channel_release_pipe_item(rcc, base, FALSE);
> -        return;
> -    }
> -    switch (base->type) {
> -        case PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST:
> -            main_channel_marshall_channels(rcc, m, base);
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_PING:
> -            main_channel_marshall_ping(rcc, m,
> -                SPICE_CONTAINEROF(base, PingPipeItem, base));
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_MOUSE_MODE:
> -            {
> -                MouseModePipeItem *item =
> -                    SPICE_CONTAINEROF(base, MouseModePipeItem, base);
> -                main_channel_marshall_mouse_mode(rcc, m, item);
> -                break;
> -            }
> -        case PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED:
> -            main_channel_marshall_agent_disconnected(rcc, m, base);
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN:
> -            main_channel_marshall_tokens(rcc, m,
> -                SPICE_CONTAINEROF(base, TokensPipeItem, base));
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_AGENT_DATA:
> -            main_channel_marshall_agent_data(rcc, m,
> -                SPICE_CONTAINEROF(base, AgentDataPipeItem, base));
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA:
> -            main_channel_marshall_migrate_data_item(rcc, m, base);
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_INIT:
> -            mcc->init_sent = TRUE;
> -            main_channel_marshall_init(rcc, m,
> -                SPICE_CONTAINEROF(base, InitPipeItem, base));
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_NOTIFY:
> -            main_channel_marshall_notify(rcc, m,
> -                SPICE_CONTAINEROF(base, NotifyPipeItem, base));
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN:
> -            main_channel_marshall_migrate_begin(m, rcc, base);
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS:
> -            main_channel_marshall_migrate_begin_seamless(m, rcc, base);
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME:
> -            main_channel_marshall_multi_media_time(rcc, m,
> -                SPICE_CONTAINEROF(base, MultiMediaTimePipeItem, base));
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST:
> -            main_channel_marshall_migrate_switch(m, rcc, base);
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_NAME:
> -            red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_NAME, base);
> -            spice_marshall_msg_main_name(m, &SPICE_CONTAINEROF(base, NamePipeItem, base)->msg);
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_UUID:
> -            red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_UUID, base);
> -            spice_marshall_msg_main_uuid(m, &SPICE_CONTAINEROF(base, UuidPipeItem, base)->msg);
> -            break;
> -        case PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS:
> -            main_channel_marshall_agent_connected(m, rcc, base);
> -            break;
> -        default:
> -            break;
> -    };
> -    red_channel_client_begin_send_message(rcc);
> -}
> -
> -static void main_channel_release_pipe_item(RedChannelClient *rcc,
> -    PipeItem *base, int item_pushed)
> -{
> -    switch (base->type) {
> -        case PIPE_ITEM_TYPE_MAIN_AGENT_DATA: {
> -                AgentDataPipeItem *data = (AgentDataPipeItem *)base;
> -
> -                data->free_data(data->data, data->opaque);
> -                break;
> -        }
> -        case PIPE_ITEM_TYPE_MAIN_NOTIFY: {
> -                NotifyPipeItem *data = (NotifyPipeItem *)base;
> -                free(data->msg);
> -                break;
> -        }
> -        default:
> -            break;
> -    }
> -    free(base);
> -}
> -
> -static void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
> -                                                         int success,
> -                                                         int seamless)
> -{
> -    spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless);
> -    if (mcc->mig_wait_connect) {
> -        MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base);
> -
> -        mcc->mig_wait_connect = FALSE;
> -        mcc->mig_connect_ok = success;
> -        spice_assert(main_channel->num_clients_mig_wait);
> -        spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
> -        if (!--main_channel->num_clients_mig_wait) {
> -            reds_on_main_migrate_connected(seamless && success);
> -        }
> -    } else {
> -        if (success) {
> -            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
> -            red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
> -        }
> -    }
> -}
> -
> -void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc,
> -                                                        uint32_t src_version)
> -{
> -    if (reds_on_migrate_dst_set_seamless(mcc, src_version)) {
> -        mcc->seamless_mig_dst = TRUE;
> -        red_channel_client_pipe_add_empty_msg(&mcc->base,
> -                                             SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK);
> -    } else {
> -        red_channel_client_pipe_add_empty_msg(&mcc->base,
> -                                              SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK);
> -    }
> -}
> -
> -void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
> -{
> -    if (!red_client_during_migrate_at_target(mcc->base.client)) {
> -        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END");
> -        return;
> -    }
> -    if (!red_channel_client_test_remote_cap(&mcc->base,
> -                                            SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
> -        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, "
> -                   "client does not support semi-seamless migration");
> -            return;
> -    }
> -    red_client_semi_seamless_migrate_complete(mcc->base.client);
> -}
> -
> -void main_channel_migrate_dst_complete(MainChannelClient *mcc)
> -{
> -    if (mcc->mig_wait_prev_complete) {
> -        if (mcc->mig_wait_prev_try_seamless) {
> -            spice_assert(mcc->base.channel->clients_num == 1);
> -            red_channel_client_pipe_add_type(&mcc->base,
> -                                             PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
> -        } else {
> -            red_channel_client_pipe_add_type(&mcc->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
> -        }
> -        mcc->mig_wait_connect = TRUE;
> -        mcc->mig_wait_prev_complete = FALSE;
> -    }
> -}
> -
> -static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type,
> -                                      void *message)
> -{
> -    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> -
> -    switch (type) {
> -    case SPICE_MSGC_MAIN_AGENT_START: {
> -        SpiceMsgcMainAgentStart *tokens;
> -
> -        spice_printerr("agent start");
> -        if (!main_chan) {
> -            return FALSE;
> -        }
> -        tokens = (SpiceMsgcMainAgentStart *)message;
> -        reds_on_main_agent_start(mcc, tokens->num_tokens);
> -        break;
> -    }
> -    case SPICE_MSGC_MAIN_AGENT_DATA: {
> -        reds_on_main_agent_data(mcc, message, size);
> -        break;
> -    }
> -    case SPICE_MSGC_MAIN_AGENT_TOKEN: {
> -        SpiceMsgcMainAgentTokens *tokens;
> -
> -        tokens = (SpiceMsgcMainAgentTokens *)message;
> -        reds_on_main_agent_tokens(mcc, tokens->num_tokens);
> -        break;
> -    }
> -    case SPICE_MSGC_MAIN_ATTACH_CHANNELS:
> -        main_channel_push_channels(mcc);
> -        break;
> -    case SPICE_MSGC_MAIN_MIGRATE_CONNECTED:
> -        main_channel_client_handle_migrate_connected(mcc,
> -                                                     TRUE /* success */,
> -                                                     FALSE /* seamless */);
> -        break;
> -    case SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS:
> -        main_channel_client_handle_migrate_connected(mcc,
> -                                                     TRUE /* success */,
> -                                                     TRUE /* seamless */);
> -        break;
> -    case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:
> -        main_channel_client_handle_migrate_connected(mcc, FALSE, FALSE);
> -        break;
> -    case SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS:
> -        main_channel_client_handle_migrate_dst_do_seamless(mcc,
> -            ((SpiceMsgcMainMigrateDstDoSeamless *)message)->src_version);
> -        break;
> -    case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST:
> -        reds_on_main_mouse_mode_request(message, size);
> -        break;
> -    case SPICE_MSGC_PONG: {
> -        SpiceMsgPing *ping = (SpiceMsgPing *)message;
> -        uint64_t roundtrip;
> -        struct timespec ts;
> -
> -        clock_gettime(CLOCK_MONOTONIC, &ts);
> -        roundtrip = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL - ping->timestamp;
> -
> -        if (ping->id == mcc->net_test_id) {
> -            switch (mcc->net_test_stage) {
> -            case NET_TEST_STAGE_WARMUP:
> -                mcc->net_test_id++;
> -                mcc->net_test_stage = NET_TEST_STAGE_LATENCY;
> -                mcc->latency = roundtrip;
> -                break;
> -            case NET_TEST_STAGE_LATENCY:
> -                mcc->net_test_id++;
> -                mcc->net_test_stage = NET_TEST_STAGE_RATE;
> -                mcc->latency = MIN(mcc->latency, roundtrip);
> -                break;
> -            case NET_TEST_STAGE_RATE:
> -                mcc->net_test_id = 0;
> -                if (roundtrip <= mcc->latency) {
> -                    // probably high load on client or server result with incorrect values
> -                    spice_printerr("net test: invalid values, latency %" PRIu64
> -                                   " roundtrip %" PRIu64 ". assuming high"
> -                                   " bandwidth", mcc->latency, roundtrip);
> -                    mcc->latency = 0;
> -                    mcc->net_test_stage = NET_TEST_STAGE_INVALID;
> -                    red_channel_client_start_connectivity_monitoring(&mcc->base,
> -                                                                     CLIENT_CONNECTIVITY_TIMEOUT);
> -                    break;
> -                }
> -                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000
> -                                        / (roundtrip - mcc->latency);
> -                mcc->net_test_stage = NET_TEST_STAGE_COMPLETE;
> -                spice_printerr("net test: latency %f ms, bitrate %"PRIu64" bps (%f Mbps)%s",
> -                           (double)mcc->latency / 1000,
> -                           mcc->bitrate_per_sec,
> -                           (double)mcc->bitrate_per_sec / 1024 / 1024,
> -                           main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : "");
> -                red_channel_client_start_connectivity_monitoring(&mcc->base,
> -                                                                 CLIENT_CONNECTIVITY_TIMEOUT);
> -                break;
> -            default:
> -                spice_printerr("invalid net test stage, ping id %d test id %d stage %d",
> -                           ping->id,
> -                           mcc->net_test_id,
> -                           mcc->net_test_stage);
> -                mcc->net_test_stage = NET_TEST_STAGE_INVALID;
> -            }
> -            break;
> -        } else {
> -            /*
> -             * channel client monitors the connectivity using ping-pong messages
> -             */
> -            red_channel_client_handle_message(rcc, size, type, message);
> -        }
> -#ifdef RED_STATISTICS
> -        reds_update_stat_value(roundtrip);
> -#endif
> -        break;
> -    }
> -    case SPICE_MSGC_DISCONNECTING:
> -        break;
> -    case SPICE_MSGC_MAIN_MIGRATE_END:
> -        main_channel_client_handle_migrate_end(mcc);
> -        break;
> -    default:
> -        return red_channel_client_handle_message(rcc, size, type, message);
> -    }
> -    return TRUE;
> -}
> -
> -static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
> -                                               uint16_t type,
> -                                               uint32_t size)
> -{
> -    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
> -    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
> -
> -    if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
> -        return reds_get_agent_data_buffer(mcc, size);
> -    } else {
> -        return main_chan->recv_buf;
> -    }
> -}
> -
> -static void main_channel_release_msg_rcv_buf(RedChannelClient *rcc,
> -                                               uint16_t type,
> -                                               uint32_t size,
> -                                               uint8_t *msg)
> -{
> -    if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
> -        reds_release_agent_data_buffer(msg);
> -    }
> -}
> -
> -static int main_channel_config_socket(RedChannelClient *rcc)
> -{
> -    return TRUE;
> -}
> -
> -static void main_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
> -{
> -}
> -
> -static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
> -{
> -    spice_debug(NULL);
> -    main_channel_push_migrate_data_item(SPICE_CONTAINEROF(rcc->channel,
> -                                        MainChannel, base));
> -    return TRUE;
> -}
> -
> -#ifdef RED_STATISTICS
> -static void do_ping_client(MainChannelClient *mcc,
> -    const char *opt, int has_interval, int interval)
> -{
> -    spice_printerr("");
> -    if (!opt) {
> -        main_channel_client_push_ping(mcc, 0);
> -    } else if (!strcmp(opt, "on")) {
> -        if (has_interval && interval > 0) {
> -            mcc->ping_interval = interval * 1000;
> -        }
> -        core->timer_start(mcc->ping_timer, mcc->ping_interval);
> -    } else if (!strcmp(opt, "off")) {
> -        core->timer_cancel(mcc->ping_timer);
> -    } else {
> -        return;
> -    }
> -}
> -
> -static void ping_timer_cb(void *opaque)
> -{
> -    MainChannelClient *mcc = opaque;
> -
> -    if (!red_channel_client_is_connected(&mcc->base)) {
> -        spice_printerr("not connected to peer, ping off");
> -        core->timer_cancel(mcc->ping_timer);
> -        return;
> -    }
> -    do_ping_client(mcc, NULL, 0, 0);
> -    core->timer_start(mcc->ping_timer, mcc->ping_interval);
> -}
> -#endif /* RED_STATISTICS */
> -
> -static MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
> -                                                     RedsStream *stream, uint32_t connection_id,
> -                                                     int num_common_caps, uint32_t *common_caps,
> -                                                     int num_caps, uint32_t *caps)
> -{
> -    MainChannelClient *mcc = (MainChannelClient*)
> -                             red_channel_client_create(sizeof(MainChannelClient), &main_chan->base,
> -                                                       client, stream, FALSE, num_common_caps,
> -                                                       common_caps, num_caps, caps);
> -    spice_assert(mcc != NULL);
> -    mcc->connection_id = connection_id;
> -    mcc->bitrate_per_sec = ~0;
> -#ifdef RED_STATISTICS
> -    if (!(mcc->ping_timer = core->timer_add(ping_timer_cb, NULL))) {
> -        spice_error("ping timer create failed");
> -    }
> -    mcc->ping_interval = PING_INTERVAL;
> -#endif
> -    return mcc;
> -}
> -
> -MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client,
> -                                     RedsStream *stream, uint32_t connection_id, int migration,
> -                                     int num_common_caps, uint32_t *common_caps, int num_caps,
> -                                     uint32_t *caps)
> -{
> -    MainChannelClient *mcc;
> -
> -    spice_assert(channel);
> -
> -    // TODO - migration - I removed it from channel creation, now put it
> -    // into usage somewhere (not an issue until we return migration to it's
> -    // former glory)
> -    spice_printerr("add main channel client");
> -    mcc = main_channel_client_create(channel, client, stream, connection_id,
> -                                     num_common_caps, common_caps,
> -                                     num_caps, caps);
> -    return mcc;
> -}
> -
> -int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen)
> -{
> -    return main_chan ? getsockname(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
> -}
> -
> -int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen)
> -{
> -    return main_chan ? getpeername(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
> -}
> -
> -// TODO: ? shouldn't it disonnect all clients? or shutdown all main_channels?
> -void main_channel_close(MainChannel *main_chan)
> -{
> -    int socketfd;
> -
> -    if (main_chan && (socketfd = red_channel_get_first_socket(&main_chan->base)) != -1) {
> -        close(socketfd);
> -    }
> -}
> -
> -int main_channel_client_is_network_info_initialized(MainChannelClient *mcc)
> -{
> -    return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE;
> -}
> -
> -int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
> -{
> -    // TODO: configurable?
> -    return mcc->bitrate_per_sec < 10 * 1024 * 1024;
> -}
> -
> -uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
> -{
> -    return mcc->bitrate_per_sec;
> -}
> -
> -uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
> -{
> -    return mcc->latency / 1000;
> -}
> -
> -static void main_channel_client_migrate(RedChannelClient *rcc)
> -{
> -    reds_on_main_channel_migrate(SPICE_CONTAINEROF(rcc, MainChannelClient, base));
> -    red_channel_client_default_migrate(rcc);
> -}
> -
> -MainChannel* main_channel_init(void)
> -{
> -    RedChannel *channel;
> -    ChannelCbs channel_cbs = { NULL, };
> -    ClientCbs client_cbs = {NULL, };
> -
> -    channel_cbs.config_socket = main_channel_config_socket;
> -    channel_cbs.on_disconnect = main_channel_client_on_disconnect;
> -    channel_cbs.send_item = main_channel_send_item;
> -    channel_cbs.hold_item = main_channel_hold_pipe_item;
> -    channel_cbs.release_item = main_channel_release_pipe_item;
> -    channel_cbs.alloc_recv_buf = main_channel_alloc_msg_rcv_buf;
> -    channel_cbs.release_recv_buf = main_channel_release_msg_rcv_buf;
> -    channel_cbs.handle_migrate_flush_mark = main_channel_handle_migrate_flush_mark;
> -    channel_cbs.handle_migrate_data = main_channel_handle_migrate_data;
> -
> -    // TODO: set the migration flag of the channel
> -    channel = red_channel_create_parser(sizeof(MainChannel), core,
> -                                        SPICE_CHANNEL_MAIN, 0,
> -                                        FALSE, /* handle_acks */
> -                                        spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL),
> -                                        main_channel_handle_parsed,
> -                                        &channel_cbs,
> -                                        SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER);
> -    spice_assert(channel);
> -    red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
> -    red_channel_set_cap(channel, SPICE_MAIN_CAP_SEAMLESS_MIGRATE);
> -
> -    client_cbs.migrate = main_channel_client_migrate;
> -    red_channel_register_client_cbs(channel, &client_cbs);
> -
> -    return (MainChannel *)channel;
> -}
> -
> -RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
> -{
> -    spice_assert(mcc);
> -    return &mcc->base;
> -}
> -
> -static int main_channel_connect_semi_seamless(MainChannel *main_channel)
> -{
> -    RingItem *client_link;
> -
> -    RING_FOREACH(client_link, &main_channel->base.clients) {
> -        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
> -                                                    base.channel_link);
> -        if (red_channel_client_test_remote_cap(&mcc->base,
> -                                               SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
> -            if (red_client_during_migrate_at_target(mcc->base.client)) {
> -                spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
> -                mcc->mig_wait_prev_complete = TRUE;
> -                mcc->mig_wait_prev_try_seamless = FALSE;
> -            } else {
> -                red_channel_client_pipe_add_type(&mcc->base,
> -                                                 PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
> -                mcc->mig_wait_connect = TRUE;
> -            }
> -            mcc->mig_connect_ok = FALSE;
> -            main_channel->num_clients_mig_wait++;
> -        }
> -    }
> -    return main_channel->num_clients_mig_wait;
> -}
> -
> -static int main_channel_connect_seamless(MainChannel *main_channel)
> -{
> -    RingItem *client_link;
> -
> -    spice_assert(main_channel->base.clients_num == 1);
> -
> -    RING_FOREACH(client_link, &main_channel->base.clients) {
> -        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
> -                                                    base.channel_link);
> -        spice_assert(red_channel_client_test_remote_cap(&mcc->base,
> -                                                        SPICE_MAIN_CAP_SEAMLESS_MIGRATE));
> -        if (red_client_during_migrate_at_target(mcc->base.client)) {
> -           spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
> -           mcc->mig_wait_prev_complete = TRUE;
> -           mcc->mig_wait_prev_try_seamless = TRUE;
> -        } else {
> -            red_channel_client_pipe_add_type(&mcc->base,
> -                                             PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
> -            mcc->mig_wait_connect = TRUE;
> -        }
> -        mcc->mig_connect_ok = FALSE;
> -        main_channel->num_clients_mig_wait++;
> -    }
> -    return main_channel->num_clients_mig_wait;
> -}
> -
> -int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target,
> -                                 int try_seamless)
> -{
> -    main_channel_fill_mig_target(main_channel, mig_target);
> -    main_channel->num_clients_mig_wait = 0;
> -
> -    if (!main_channel_is_connected(main_channel)) {
> -        return 0;
> -    }
> -
> -    if (!try_seamless) {
> -        return main_channel_connect_semi_seamless(main_channel);
> -    } else {
> -        RingItem *client_item;
> -        MainChannelClient *mcc;
> -
> -        client_item = ring_get_head(&main_channel->base.clients);
> -        mcc = SPICE_CONTAINEROF(client_item, MainChannelClient, base.channel_link);
> -
> -        if (!red_channel_client_test_remote_cap(&mcc->base,
> -                                                SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) {
> -            return main_channel_connect_semi_seamless(main_channel);
> -        } else {
> -            return main_channel_connect_seamless(main_channel);
> -        }
> -    }
> -
> -}
> -
> -void main_channel_migrate_cancel_wait(MainChannel *main_chan)
> -{
> -    RingItem *client_link;
> -
> -    RING_FOREACH(client_link, &main_chan->base.clients) {
> -        MainChannelClient *mcc;
> -
> -        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
> -        if (mcc->mig_wait_connect) {
> -            spice_printerr("client %p cancel wait connect", mcc->base.client);
> -            mcc->mig_wait_connect = FALSE;
> -            mcc->mig_connect_ok = FALSE;
> -        }
> -        mcc->mig_wait_prev_complete = FALSE;
> -    }
> -    main_chan->num_clients_mig_wait = 0;
> -}
> -
> -int main_channel_migrate_src_complete(MainChannel *main_chan, int success)
> -{
> -    RingItem *client_link;
> -    int semi_seamless_count = 0;
> -
> -    spice_printerr("");
> -
> -    if (ring_is_empty(&main_chan->base.clients)) {
> -        spice_printerr("no peer connected");
> -        return 0;
> -    }
> -
> -    RING_FOREACH(client_link, &main_chan->base.clients) {
> -        MainChannelClient *mcc;
> -        int semi_seamless_support;
> -
> -        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
> -        semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base,
> -                                                   SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
> -        if (semi_seamless_support && mcc->mig_connect_ok) {
> -            if (success) {
> -                spice_printerr("client %p MIGRATE_END", mcc->base.client);
> -                red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END);
> -                semi_seamless_count++;
> -            } else {
> -                spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
> -                red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
> -            }
> -        } else {
> -            if (success) {
> -                spice_printerr("client %p SWITCH_HOST", mcc->base.client);
> -                red_channel_client_pipe_add_type(&mcc->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
> -            }
> -        }
> -        mcc->mig_connect_ok = FALSE;
> -        mcc->mig_wait_connect = FALSE;
> -   }
> -   return semi_seamless_count;
> -}
> diff --git a/server/main_channel.h b/server/main_channel.h
> deleted file mode 100644
> index 9bd20f1..0000000
> --- a/server/main_channel.h
> +++ /dev/null
> @@ -1,103 +0,0 @@
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -
> -#ifndef __MAIN_CHANNEL_H__
> -#define __MAIN_CHANNEL_H__
> -
> -#include <stdint.h>
> -#include <spice/vd_agent.h>
> -#include "common/marshaller.h"
> -#include "red_channel.h"
> -
> -// TODO: Defines used to calculate receive buffer size, and also by reds.c
> -// other options: is to make a reds_main_consts.h, to duplicate defines.
> -#define REDS_AGENT_WINDOW_SIZE 10
> -#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
> -
> -// approximate max receive message size for main channel
> -#define MAIN_CHANNEL_RECEIVE_BUF_SIZE \
> -    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
> -
> -struct RedsMigSpice {
> -    char *host;
> -    char *cert_subject;
> -    int port;
> -    int sport;
> -};
> -typedef struct RedsMigSpice RedsMigSpice;
> -
> -typedef struct MainChannel {
> -    RedChannel base;
> -    uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE];
> -    RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?)
> -    int num_clients_mig_wait;
> -} MainChannel;
> -
> -
> -MainChannel *main_channel_init(void);
> -RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id);
> -/* This is a 'clone' from the reds.h Channel.link callback to allow passing link_id */
> -MainChannelClient *main_channel_link(MainChannel *, RedClient *client,
> -     RedsStream *stream, uint32_t link_id, int migration, int num_common_caps,
> -     uint32_t *common_caps, int num_caps, uint32_t *caps);
> -void main_channel_close(MainChannel *main_chan); // not destroy, just socket close
> -void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int is_client_mouse_allowed);
> -void main_channel_push_agent_connected(MainChannel *main_chan);
> -void main_channel_push_agent_disconnected(MainChannel *main_chan);
> -void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
> -void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
> -                                         spice_marshaller_item_free_func free_data, void *opaque);
> -void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate);
> -// TODO: huge. Consider making a reds_* interface for these functions
> -// and calling from main.
> -void main_channel_push_init(MainChannelClient *mcc, int display_channels_hint,
> -    int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time,
> -    int ram_hint);
> -void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg);
> -void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
> -int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
> -int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
> -
> -/*
> - * return TRUE if network test had been completed successfully.
> - * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set to 0
> - */
> -int main_channel_client_is_network_info_initialized(MainChannelClient *mcc);
> -int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
> -uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
> -uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
> -
> -int main_channel_is_connected(MainChannel *main_chan);
> -RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
> -
> -/* switch host migration */
> -void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target);
> -
> -/* semi seamless migration */
> -
> -/* returns the number of clients that we are waiting for their connection.
> - * try_seamless = 'true' when the seamless-migration=on in qemu command line */
> -int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target,
> -                                 int try_seamless);
> -void main_channel_migrate_cancel_wait(MainChannel *main_chan);
> -/* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/
> -int main_channel_migrate_src_complete(MainChannel *main_chan, int success);
> -void main_channel_migrate_dst_complete(MainChannelClient *mcc);
> -void main_channel_push_name(MainChannelClient *mcc, const char *name);
> -void main_channel_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]);
> -
> -#endif
> diff --git a/server/main_dispatcher.c b/server/main_dispatcher.c
> deleted file mode 100644
> index 6ad9d89..0000000
> --- a/server/main_dispatcher.c
> +++ /dev/null
> @@ -1,217 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009-2015 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#include <config.h>
> -#include <unistd.h>
> -#include <string.h>
> -#include <errno.h>
> -#include <pthread.h>
> -
> -#include "red_common.h"
> -#include "dispatcher.h"
> -#include "main_dispatcher.h"
> -#include "red_channel.h"
> -#include "reds.h"
> -
> -/*
> - * Main Dispatcher
> - * ===============
> - *
> - * Communication channel between any non main thread and the main thread.
> - *
> - * The main thread is that from which spice_server_init is called.
> - *
> - * Messages are single sized, sent from the non-main thread to the main-thread.
> - * No acknowledge is sent back. This prevents a possible deadlock with the main
> - * thread already waiting on a response for the existing red_dispatcher used
> - * by the worker thread.
> - *
> - * All events have three functions:
> - * main_dispatcher_<event_name> - non static, public function
> - * main_dispatcher_self_<event_name> - handler for main thread
> - * main_dispatcher_handle_<event_name> - handler for callback from main thread
> - *   seperate from self because it may send an ack or do other work in the future.
> - */
> -
> -typedef struct {
> -    Dispatcher base;
> -    SpiceCoreInterface *core;
> -} MainDispatcher;
> -
> -MainDispatcher main_dispatcher;
> -
> -enum {
> -    MAIN_DISPATCHER_CHANNEL_EVENT = 0,
> -    MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
> -    MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
> -    MAIN_DISPATCHER_CLIENT_DISCONNECT,
> -
> -    MAIN_DISPATCHER_NUM_MESSAGES
> -};
> -
> -typedef struct MainDispatcherChannelEventMessage {
> -    int event;
> -    SpiceChannelEventInfo *info;
> -} MainDispatcherChannelEventMessage;
> -
> -typedef struct MainDispatcherMigrateSeamlessDstCompleteMessage {
> -    RedClient *client;
> -} MainDispatcherMigrateSeamlessDstCompleteMessage;
> -
> -typedef struct MainDispatcherMmTimeLatencyMessage {
> -    RedClient *client;
> -    uint32_t latency;
> -} MainDispatcherMmTimeLatencyMessage;
> -
> -typedef struct MainDispatcherClientDisconnectMessage {
> -    RedClient *client;
> -} MainDispatcherClientDisconnectMessage;
> -
> -/* channel_event - calls core->channel_event, must be done in main thread */
> -static void main_dispatcher_self_handle_channel_event(
> -                                                int event,
> -                                                SpiceChannelEventInfo *info)
> -{
> -    reds_handle_channel_event(event, info);
> -}
> -
> -static void main_dispatcher_handle_channel_event(void *opaque,
> -                                                 void *payload)
> -{
> -    MainDispatcherChannelEventMessage *channel_event = payload;
> -
> -    main_dispatcher_self_handle_channel_event(channel_event->event,
> -                                              channel_event->info);
> -}
> -
> -void main_dispatcher_channel_event(int event, SpiceChannelEventInfo *info)
> -{
> -    MainDispatcherChannelEventMessage msg = {0,};
> -
> -    if (pthread_self() == main_dispatcher.base.self) {
> -        main_dispatcher_self_handle_channel_event(event, info);
> -        return;
> -    }
> -    msg.event = event;
> -    msg.info = info;
> -    dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_CHANNEL_EVENT,
> -                            &msg);
> -}
> -
> -
> -static void main_dispatcher_handle_migrate_complete(void *opaque,
> -                                                    void *payload)
> -{
> -    MainDispatcherMigrateSeamlessDstCompleteMessage *mig_complete = payload;
> -
> -    reds_on_client_seamless_migrate_complete(mig_complete->client);
> -    red_client_unref(mig_complete->client);
> -}
> -
> -static void main_dispatcher_handle_mm_time_latency(void *opaque,
> -                                                   void *payload)
> -{
> -    MainDispatcherMmTimeLatencyMessage *msg = payload;
> -    reds_set_client_mm_time_latency(msg->client, msg->latency);
> -    red_client_unref(msg->client);
> -}
> -
> -static void main_dispatcher_handle_client_disconnect(void *opaque,
> -                                                     void *payload)
> -{
> -    MainDispatcherClientDisconnectMessage *msg = payload;
> -
> -    spice_debug("client=%p", msg->client);
> -    reds_client_disconnect(msg->client);
> -    red_client_unref(msg->client);
> -}
> -
> -void main_dispatcher_seamless_migrate_dst_complete(RedClient *client)
> -{
> -    MainDispatcherMigrateSeamlessDstCompleteMessage msg;
> -
> -    if (pthread_self() == main_dispatcher.base.self) {
> -        reds_on_client_seamless_migrate_complete(client);
> -        return;
> -    }
> -
> -    msg.client = red_client_ref(client);
> -    dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
> -                            &msg);
> -}
> -
> -void main_dispatcher_set_mm_time_latency(RedClient *client, uint32_t latency)
> -{
> -    MainDispatcherMmTimeLatencyMessage msg;
> -
> -    if (pthread_self() == main_dispatcher.base.self) {
> -        reds_set_client_mm_time_latency(client, latency);
> -        return;
> -    }
> -
> -    msg.client = red_client_ref(client);
> -    msg.latency = latency;
> -    dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
> -                            &msg);
> -}
> -
> -void main_dispatcher_client_disconnect(RedClient *client)
> -{
> -    MainDispatcherClientDisconnectMessage msg;
> -
> -    if (!client->disconnecting) {
> -        spice_debug("client %p", client);
> -        msg.client = red_client_ref(client);
> -        dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_CLIENT_DISCONNECT,
> -                                &msg);
> -    } else {
> -        spice_debug("client %p already during disconnection", client);
> -    }
> -}
> -
> -static void dispatcher_handle_read(int fd, int event, void *opaque)
> -{
> -    Dispatcher *dispatcher = opaque;
> -
> -    dispatcher_handle_recv_read(dispatcher);
> -}
> -
> -/*
> - * FIXME:
> - * Reds routines shouldn't be exposed. Instead reds.c should register the callbacks,
> - * and the corresponding operations should be made only via main_dispatcher.
> - */
> -void main_dispatcher_init(SpiceCoreInterface *core)
> -{
> -    memset(&main_dispatcher, 0, sizeof(main_dispatcher));
> -    main_dispatcher.core = core;
> -    dispatcher_init(&main_dispatcher.base, MAIN_DISPATCHER_NUM_MESSAGES, &main_dispatcher.base);
> -    core->watch_add(main_dispatcher.base.recv_fd, SPICE_WATCH_EVENT_READ,
> -                    dispatcher_handle_read, &main_dispatcher.base);
> -    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_CHANNEL_EVENT,
> -                                main_dispatcher_handle_channel_event,
> -                                sizeof(MainDispatcherChannelEventMessage), 0 /* no ack */);
> -    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
> -                                main_dispatcher_handle_migrate_complete,
> -                                sizeof(MainDispatcherMigrateSeamlessDstCompleteMessage), 0 /* no ack */);
> -    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
> -                                main_dispatcher_handle_mm_time_latency,
> -                                sizeof(MainDispatcherMmTimeLatencyMessage), 0 /* no ack */);
> -    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_CLIENT_DISCONNECT,
> -                                main_dispatcher_handle_client_disconnect,
> -                                sizeof(MainDispatcherClientDisconnectMessage), 0 /* no ack */);
> -}
> diff --git a/server/main_dispatcher.h b/server/main_dispatcher.h
> deleted file mode 100644
> index af40093..0000000
> --- a/server/main_dispatcher.h
> +++ /dev/null
> @@ -1,36 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009-2015 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifndef MAIN_DISPATCHER_H
> -#define MAIN_DISPATCHER_H
> -
> -#include <spice.h>
> -#include "red_channel.h"
> -
> -void main_dispatcher_channel_event(int event, SpiceChannelEventInfo *info);
> -void main_dispatcher_seamless_migrate_dst_complete(RedClient *client);
> -void main_dispatcher_set_mm_time_latency(RedClient *client, uint32_t latency);
> -/*
> - * Disconnecting the client is always executed asynchronously,
> - * in order to protect from expired references in the routines
> - * that triggered the client destruction.
> - */
> -void main_dispatcher_client_disconnect(RedClient *client);
> -
> -void main_dispatcher_init(SpiceCoreInterface *core);
> -
> -#endif //MAIN_DISPATCHER_H
> diff --git a/server/memslot.c b/server/memslot.c
> new file mode 100644
> index 0000000..e7ee04c
> --- /dev/null
> +++ b/server/memslot.c
> @@ -0,0 +1,184 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009,2010 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <inttypes.h>
> +
> +#include "red_common.h"
> +#include "memslot.h"
> +
> +static unsigned long __get_clean_virt(RedMemSlotInfo *info, QXLPHYSICAL addr)
> +{
> +    return addr & info->memslot_clean_virt_mask;
> +}
> +
> +static void print_memslots(RedMemSlotInfo *info)
> +{
> +    int i;
> +    int x;
> +
> +    for (i = 0; i < info->num_memslots_groups; ++i) {
> +        for (x = 0; x < info->num_memslots; ++x) {
> +            if (!info->mem_slots[i][x].virt_start_addr &&
> +                !info->mem_slots[i][x].virt_end_addr) {
> +                continue;
> +            }
> +            printf("id %d, group %d, virt start %lx, virt end %lx, generation %u, delta %lx\n",
> +                   x, i, info->mem_slots[i][x].virt_start_addr,
> +                   info->mem_slots[i][x].virt_end_addr, info->mem_slots[i][x].generation,
> +                   info->mem_slots[i][x].address_delta);
> +            }
> +    }
> +}
> +
> +/* return 1 if validation successfull, 0 otherwise */
> +int memslot_validate_virt(RedMemSlotInfo *info, unsigned long virt, int slot_id,
> +                          uint32_t add_size, uint32_t group_id)
> +{
> +    MemSlot *slot;
> +
> +    slot = &info->mem_slots[group_id][slot_id];
> +    if ((virt + add_size) < virt) {
> +        spice_critical("virtual address overlap");
> +        return 0;
> +    }
> +
> +    if (virt < slot->virt_start_addr || (virt + add_size) > slot->virt_end_addr) {
> +        print_memslots(info);
> +        spice_critical("virtual address out of range\n"
> +              "    virt=0x%lx+0x%x slot_id=%d group_id=%d\n"
> +              "    slot=0x%lx-0x%lx delta=0x%lx",
> +              virt, add_size, slot_id, group_id,
> +              slot->virt_start_addr, slot->virt_end_addr, slot->address_delta);
> +        return 0;
> +    }
> +    return 1;
> +}
> +
> +/*
> + * return virtual address if successful, which may be 0.
> + * returns 0 and sets error to 1 if an error condition occurs.
> + */
> +unsigned long memslot_get_virt(RedMemSlotInfo *info, QXLPHYSICAL addr, uint32_t add_size,
> +                               int group_id, int *error)
> +{
> +    int slot_id;
> +    int generation;
> +    unsigned long h_virt;
> +
> +    MemSlot *slot;
> +
> +    *error = 0;
> +    if (group_id > info->num_memslots_groups) {
> +        spice_critical("group_id too big");
> +        *error = 1;
> +        return 0;
> +    }
> +
> +    slot_id = memslot_get_id(info, addr);
> +    if (slot_id > info->num_memslots) {
> +        print_memslots(info);
> +        spice_critical("slot_id %d too big, addr=%" PRIx64, slot_id, addr);
> +        *error = 1;
> +        return 0;
> +    }
> +
> +    slot = &info->mem_slots[group_id][slot_id];
> +
> +    generation = memslot_get_generation(info, addr);
> +    if (generation != slot->generation) {
> +        print_memslots(info);
> +        spice_critical("address generation is not valid, group_id %d, slot_id %d, gen %d, slot_gen %d\n",
> +              group_id, slot_id, generation, slot->generation);
> +        *error = 1;
> +        return 0;
> +    }
> +
> +    h_virt = __get_clean_virt(info, addr);
> +    h_virt += slot->address_delta;
> +
> +    if (!memslot_validate_virt(info, h_virt, slot_id, add_size, group_id)) {
> +        *error = 1;
> +        return 0;
> +    }
> +
> +    return h_virt;
> +}
> +
> +void memslot_info_init(RedMemSlotInfo *info,
> +                       uint32_t num_groups, uint32_t num_slots,
> +                       uint8_t generation_bits,
> +                       uint8_t id_bits,
> +                       uint8_t internal_groupslot_id)
> +{
> +    uint32_t i;
> +
> +    spice_return_if_fail(num_slots > 0);
> +    spice_return_if_fail(num_groups > 0);
> +
> +    info->num_memslots_groups = num_groups;
> +    info->num_memslots = num_slots;
> +    info->generation_bits = generation_bits;
> +    info->mem_slot_bits = id_bits;
> +    info->internal_groupslot_id = internal_groupslot_id;
> +
> +    info->mem_slots = spice_new(MemSlot *, num_groups);
> +
> +    for (i = 0; i < num_groups; ++i) {
> +        info->mem_slots[i] = spice_new0(MemSlot, num_slots);
> +    }
> +
> +    /* TODO: use QXLPHYSICAL_BITS */
> +    info->memslot_id_shift = 64 - info->mem_slot_bits;
> +    info->memslot_gen_shift = 64 - (info->mem_slot_bits + info->generation_bits);
> +    info->memslot_gen_mask = ~((QXLPHYSICAL)-1 << info->generation_bits);
> +    info->memslot_clean_virt_mask = (((QXLPHYSICAL)(-1)) >>
> +                                       (info->mem_slot_bits + info->generation_bits));
> +}
> +
> +void memslot_info_add_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id,
> +                           uint64_t addr_delta, unsigned long virt_start, unsigned long virt_end,
> +                           uint32_t generation)
> +{
> +    spice_return_if_fail(info->num_memslots_groups > slot_group_id);
> +    spice_return_if_fail(info->num_memslots > slot_id);
> +
> +    info->mem_slots[slot_group_id][slot_id].address_delta = addr_delta;
> +    info->mem_slots[slot_group_id][slot_id].virt_start_addr = virt_start;
> +    info->mem_slots[slot_group_id][slot_id].virt_end_addr = virt_end;
> +    info->mem_slots[slot_group_id][slot_id].generation = generation;
> +}
> +
> +void memslot_info_del_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id)
> +{
> +    spice_return_if_fail(info->num_memslots_groups > slot_group_id);
> +    spice_return_if_fail(info->num_memslots > slot_id);
> +
> +    info->mem_slots[slot_group_id][slot_id].virt_start_addr = 0;
> +    info->mem_slots[slot_group_id][slot_id].virt_end_addr = 0;
> +}
> +
> +void memslot_info_reset(RedMemSlotInfo *info)
> +{
> +        uint32_t i;
> +        for (i = 0; i < info->num_memslots_groups; ++i) {
> +            memset(info->mem_slots[i], 0, sizeof(MemSlot) * info->num_memslots);
> +        }
> +}
> diff --git a/server/memslot.h b/server/memslot.h
> new file mode 100644
> index 0000000..1fba4b8
> --- /dev/null
> +++ b/server/memslot.h
> @@ -0,0 +1,72 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009,2010 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +
> +#ifndef MEMSLOT_H_
> +#define MEMSLOT_H_
> +
> +#include "red_common.h"
> +
> +#include <spice/qxl_dev.h>
> +
> +typedef struct MemSlot {
> +    int generation;
> +    unsigned long virt_start_addr;
> +    unsigned long virt_end_addr;
> +    long address_delta;
> +} MemSlot;
> +
> +typedef struct RedMemSlotInfo {
> +    MemSlot **mem_slots;
> +    uint32_t num_memslots_groups;
> +    uint32_t num_memslots;
> +    uint8_t mem_slot_bits;
> +    uint8_t generation_bits;
> +    uint8_t memslot_id_shift;
> +    uint8_t memslot_gen_shift;
> +    uint8_t internal_groupslot_id;
> +    unsigned long memslot_gen_mask;
> +    unsigned long memslot_clean_virt_mask;
> +} RedMemSlotInfo;
> +
> +static inline int memslot_get_id(RedMemSlotInfo *info, uint64_t addr)
> +{
> +    return addr >> info->memslot_id_shift;
> +}
> +
> +static inline int memslot_get_generation(RedMemSlotInfo *info, uint64_t addr)
> +{
> +    return (addr >> info->memslot_gen_shift) & info->memslot_gen_mask;
> +}
> +
> +int memslot_validate_virt(RedMemSlotInfo *info, unsigned long virt, int slot_id,
> +                          uint32_t add_size, uint32_t group_id);
> +unsigned long memslot_get_virt(RedMemSlotInfo *info, QXLPHYSICAL addr, uint32_t add_size,
> +                       int group_id, int *error);
> +
> +void memslot_info_init(RedMemSlotInfo *info,
> +                       uint32_t num_groups, uint32_t num_slots,
> +                       uint8_t generation_bits,
> +                       uint8_t id_bits,
> +                       uint8_t internal_groupslot_id);
> +void memslot_info_add_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id,
> +                           uint64_t addr_delta, unsigned long virt_start, unsigned long virt_end,
> +                           uint32_t generation);
> +void memslot_info_del_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id);
> +void memslot_info_reset(RedMemSlotInfo *info);
> +
> +#endif
> diff --git a/server/migration-protocol.h b/server/migration-protocol.h
> new file mode 100644
> index 0000000..c1d97ef
> --- /dev/null
> +++ b/server/migration-protocol.h
> @@ -0,0 +1,213 @@
> +/*
> +   Copyright (C) 2012 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +
> +#ifndef _H_MIGRATION_PROTOCOL
> +#define _H_MIGRATION_PROTOCOL
> +
> +#include <spice/macros.h>
> +#include <spice/vd_agent.h>
> +#include "glz-encoder-dict.h"
> +
> +/* ************************************************
> + * src-server to dst-server migration data messages
> + * ************************************************/
> +
> +/* increase the version when the version of any
> + * of the migration data messages is increased */
> +#define SPICE_MIGRATION_PROTOCOL_VERSION 1
> +
> +typedef struct __attribute__ ((__packed__)) SpiceMigrateDataHeader {
> +    uint32_t magic;
> +    uint32_t version;
> +} SpiceMigrateDataHeader;
> +
> +/* ********************
> + * Char device base
> + * *******************/
> +
> +/* increase the version of descendent char devices when this
> + * version is increased */
> +#define SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION 1
> +
> +/* Should be the first field of any of the char_devices migration data (see write_data_ptr) */
> +typedef struct __attribute__ ((__packed__)) SpiceMigrateDataCharDevice {
> +    uint32_t version;
> +    uint8_t connected;
> +    uint32_t num_client_tokens;
> +    uint32_t num_send_tokens;
> +    uint32_t write_size; /* write to dev */
> +    uint32_t write_num_client_tokens; /* how many messages from the client are part of the write_data */
> +    uint32_t write_data_ptr; /* offset from
> +                                SpiceMigrateDataCharDevice - sizeof(SpiceMigrateDataHeader) */
> +} SpiceMigrateDataCharDevice;
> +
> +/* ********
> + * spicevmc
> + * ********/
> +
> +#define SPICE_MIGRATE_DATA_SPICEVMC_VERSION 1 /* NOTE: increase version when CHAR_DEVICE_VERSION
> +                                                 is increased */
> +#define SPICE_MIGRATE_DATA_SPICEVMC_MAGIC SPICE_MAGIC_CONST("SVMD")
> +typedef struct __attribute__ ((__packed__)) SpiceMigrateDataSpiceVmc {
> +    SpiceMigrateDataCharDevice base;
> +} SpiceMigrateDataSpiceVmc;
> +
> +/* *********
> + * smartcard
> + * *********/
> +
> +#define SPICE_MIGRATE_DATA_SMARTCARD_VERSION 1 /* NOTE: increase version when CHAR_DEVICE_VERSION
> +                                                  is increased */
> +#define SPICE_MIGRATE_DATA_SMARTCARD_MAGIC SPICE_MAGIC_CONST("SCMD")
> +typedef struct __attribute__ ((__packed__)) SpiceMigrateDataSmartcard {
> +    SpiceMigrateDataCharDevice base;
> +    uint8_t reader_added;
> +    uint32_t read_size; /* partial data read from dev */
> +    uint32_t read_data_ptr;
> +} SpiceMigrateDataSmartcard;
> +
> +/* *********************************
> + * main channel (mainly guest agent)
> + * *********************************/
> +#define SPICE_MIGRATE_DATA_MAIN_VERSION 1 /* NOTE: increase version when CHAR_DEVICE_VERSION
> +                                             is increased */
> +#define SPICE_MIGRATE_DATA_MAIN_MAGIC SPICE_MAGIC_CONST("MNMD")
> +
> +typedef struct __attribute__ ((__packed__)) SpiceMigrateDataMain {
> +    SpiceMigrateDataCharDevice agent_base;
> +    uint8_t client_agent_started; /* for discarding messages */
> +
> +    struct __attribute__ ((__packed__)) {
> +        /* partial data read from device. Such data is stored only
> +         * if the chunk header or the entire msg header haven't yet been read completely.
> +         * Once the headers are read, partial reads of chunks can be sent as
> +         * smaller chunks to the client, without the roundtrip overhead of migration data */
> +        uint32_t chunk_header_size;
> +        VDIChunkHeader chunk_header;
> +        uint8_t msg_header_done;
> +        uint32_t msg_header_partial_len;
> +        uint32_t msg_header_ptr;
> +        uint32_t msg_remaining;
> +        uint8_t msg_filter_result;
> +    } agent2client;
> +
> +    struct __attribute__ ((__packed__)) {
> +        uint32_t msg_remaining;
> +        uint8_t msg_filter_result;
> +    } client2agent;
> +} SpiceMigrateDataMain;
> +
> +/* ****************
> + * display channel
> + * ***************/
> +
> +#define SPICE_MIGRATE_DATA_DISPLAY_VERSION 1
> +#define SPICE_MIGRATE_DATA_DISPLAY_MAGIC SPICE_MAGIC_CONST("DCMD")
> +
> +/*
> + * TODO: store the cache and dictionary data only in one channel (the
> + *       freezer).
> + * TODO: optimizations: don't send surfaces information if it will be faster
> + *       to resend the surfaces on-demand.
> + * */
> +#define MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS 4
> +
> +typedef struct __attribute__ ((__packed__)) SpiceMigrateDataDisplay {
> +    uint64_t message_serial;
> +    uint8_t low_bandwidth_setting;
> +
> +    /*
> +     * Synchronizing the shared pixmap cache.
> +     * For now, the cache is not migrated, and instead, we reset it and send
> +     * SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS to the client.
> +     * In order to keep the client and server caches consistent:
> +     * The channel which freezed the cache on the src side, unfreezes it
> +     * on the dest side, and increases its generation (see 'reset' in red_client_shared_cach.h).
> +     * In order to enforce that images that are added to the cache by other channels
> +     * will reach the client only after SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS,
> +     * we send SPICE_MSG_WAIT_FOR_CHANNELS
> +     * (see the generation mismatch handling in 'add' in red_client_shared_cach.h).
> +     */
> +    uint8_t pixmap_cache_id;
> +    int64_t pixmap_cache_size;
> +    uint8_t pixmap_cache_freezer;
> +    uint64_t pixmap_cache_clients[MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS];
> +
> +    uint8_t glz_dict_id;
> +    GlzEncDictRestoreData glz_dict_data;
> +
> +    uint32_t surfaces_at_client_ptr; /* reference to MigrateDisplaySurfacesAtClientLossless/Lossy.
> +                                        Lossy: when jpeg-wan-compression(qemu cmd line)=always
> +                                        or when jpeg-wan-compression=auto,
> +                                        and low_bandwidth_setting=TRUE */
> +
> +} SpiceMigrateDataDisplay;
> +
> +typedef struct __attribute__ ((__packed__)) SpiceMigrateDataRect {
> +    int32_t left;
> +    int32_t top;
> +    int32_t right;
> +    int32_t bottom;
> +} SpiceMigrateDataRect;
> +
> +typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfaceLossless {
> +    uint32_t id;
> +} MigrateDisplaySurfaceLossless;
> +
> +typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfaceLossy {
> +    uint32_t id;
> +    SpiceMigrateDataRect lossy_rect;
> +} MigrateDisplaySurfaceLossy;
> +
> +typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfacesAtClientLossless {
> +    uint32_t num_surfaces;
> +    MigrateDisplaySurfaceLossless surfaces[0];
> +} MigrateDisplaySurfacesAtClientLossless;
> +
> +typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfacesAtClientLossy {
> +    uint32_t num_surfaces;
> +    MigrateDisplaySurfaceLossy surfaces[0];
> +} MigrateDisplaySurfacesAtClientLossy;
> +
> +/* ****************
> + * inputs channel
> + * ***************/
> +
> +#define SPICE_MIGRATE_DATA_INPUTS_VERSION 1
> +#define SPICE_MIGRATE_DATA_INPUTS_MAGIC SPICE_MAGIC_CONST("ICMD")
> +
> +
> +typedef struct __attribute__ ((__packed__)) SpiceMigrateDataInputs {
> +    uint16_t motion_count;
> +} SpiceMigrateDataInputs;
> +
> +static inline int migration_protocol_validate_header(SpiceMigrateDataHeader *header,
> +                                                     uint32_t magic,
> +                                                     uint32_t version)
> +{
> +    if (header->magic != magic) {
> +        spice_error("bad magic %u (!= %u)", header->magic, magic);
> +        return FALSE;
> +    }
> +    if (header->version > version) {
> +        spice_error("unsupported version %u (> %u)", header->version, version);
> +        return FALSE;
> +    }
> +    return TRUE;
> +}
> +
> +#endif
> diff --git a/server/migration_protocol.h b/server/migration_protocol.h
> deleted file mode 100644
> index 21d3ec8..0000000
> --- a/server/migration_protocol.h
> +++ /dev/null
> @@ -1,213 +0,0 @@
> -/*
> -   Copyright (C) 2012 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -
> -#ifndef _H_MIGRATION_PROTOCOL
> -#define _H_MIGRATION_PROTOCOL
> -
> -#include <spice/macros.h>
> -#include <spice/vd_agent.h>
> -#include "glz_encoder_dictionary.h"
> -
> -/* ************************************************
> - * src-server to dst-server migration data messages
> - * ************************************************/
> -
> -/* increase the version when the version of any
> - * of the migration data messages is increased */
> -#define SPICE_MIGRATION_PROTOCOL_VERSION 1
> -
> -typedef struct __attribute__ ((__packed__)) SpiceMigrateDataHeader {
> -    uint32_t magic;
> -    uint32_t version;
> -} SpiceMigrateDataHeader;
> -
> -/* ********************
> - * Char device base
> - * *******************/
> -
> -/* increase the version of descendent char devices when this
> - * version is increased */
> -#define SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION 1
> -
> -/* Should be the first field of any of the char_devices migration data (see write_data_ptr) */
> -typedef struct __attribute__ ((__packed__)) SpiceMigrateDataCharDevice {
> -    uint32_t version;
> -    uint8_t connected;
> -    uint32_t num_client_tokens;
> -    uint32_t num_send_tokens;
> -    uint32_t write_size; /* write to dev */
> -    uint32_t write_num_client_tokens; /* how many messages from the client are part of the write_data */
> -    uint32_t write_data_ptr; /* offset from
> -                                SpiceMigrateDataCharDevice - sizeof(SpiceMigrateDataHeader) */
> -} SpiceMigrateDataCharDevice;
> -
> -/* ********
> - * spicevmc
> - * ********/
> -
> -#define SPICE_MIGRATE_DATA_SPICEVMC_VERSION 1 /* NOTE: increase version when CHAR_DEVICE_VERSION
> -                                                 is increased */
> -#define SPICE_MIGRATE_DATA_SPICEVMC_MAGIC SPICE_MAGIC_CONST("SVMD")
> -typedef struct __attribute__ ((__packed__)) SpiceMigrateDataSpiceVmc {
> -    SpiceMigrateDataCharDevice base;
> -} SpiceMigrateDataSpiceVmc;
> -
> -/* *********
> - * smartcard
> - * *********/
> -
> -#define SPICE_MIGRATE_DATA_SMARTCARD_VERSION 1 /* NOTE: increase version when CHAR_DEVICE_VERSION
> -                                                  is increased */
> -#define SPICE_MIGRATE_DATA_SMARTCARD_MAGIC SPICE_MAGIC_CONST("SCMD")
> -typedef struct __attribute__ ((__packed__)) SpiceMigrateDataSmartcard {
> -    SpiceMigrateDataCharDevice base;
> -    uint8_t reader_added;
> -    uint32_t read_size; /* partial data read from dev */
> -    uint32_t read_data_ptr;
> -} SpiceMigrateDataSmartcard;
> -
> -/* *********************************
> - * main channel (mainly guest agent)
> - * *********************************/
> -#define SPICE_MIGRATE_DATA_MAIN_VERSION 1 /* NOTE: increase version when CHAR_DEVICE_VERSION
> -                                             is increased */
> -#define SPICE_MIGRATE_DATA_MAIN_MAGIC SPICE_MAGIC_CONST("MNMD")
> -
> -typedef struct __attribute__ ((__packed__)) SpiceMigrateDataMain {
> -    SpiceMigrateDataCharDevice agent_base;
> -    uint8_t client_agent_started; /* for discarding messages */
> -
> -    struct __attribute__ ((__packed__)) {
> -        /* partial data read from device. Such data is stored only
> -         * if the chunk header or the entire msg header haven't yet been read completely.
> -         * Once the headers are read, partial reads of chunks can be sent as
> -         * smaller chunks to the client, without the roundtrip overhead of migration data */
> -        uint32_t chunk_header_size;
> -        VDIChunkHeader chunk_header;
> -        uint8_t msg_header_done;
> -        uint32_t msg_header_partial_len;
> -        uint32_t msg_header_ptr;
> -        uint32_t msg_remaining;
> -        uint8_t msg_filter_result;
> -    } agent2client;
> -
> -    struct __attribute__ ((__packed__)) {
> -        uint32_t msg_remaining;
> -        uint8_t msg_filter_result;
> -    } client2agent;
> -} SpiceMigrateDataMain;
> -
> -/* ****************
> - * display channel
> - * ***************/
> -
> -#define SPICE_MIGRATE_DATA_DISPLAY_VERSION 1
> -#define SPICE_MIGRATE_DATA_DISPLAY_MAGIC SPICE_MAGIC_CONST("DCMD")
> -
> -/*
> - * TODO: store the cache and dictionary data only in one channel (the
> - *       freezer).
> - * TODO: optimizations: don't send surfaces information if it will be faster
> - *       to resend the surfaces on-demand.
> - * */
> -#define MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS 4
> -
> -typedef struct __attribute__ ((__packed__)) SpiceMigrateDataDisplay {
> -    uint64_t message_serial;
> -    uint8_t low_bandwidth_setting;
> -
> -    /*
> -     * Synchronizing the shared pixmap cache.
> -     * For now, the cache is not migrated, and instead, we reset it and send
> -     * SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS to the client.
> -     * In order to keep the client and server caches consistent:
> -     * The channel which freezed the cache on the src side, unfreezes it
> -     * on the dest side, and increases its generation (see 'reset' in red_client_shared_cach.h).
> -     * In order to enforce that images that are added to the cache by other channels
> -     * will reach the client only after SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS,
> -     * we send SPICE_MSG_WAIT_FOR_CHANNELS
> -     * (see the generation mismatch handling in 'add' in red_client_shared_cach.h).
> -     */
> -    uint8_t pixmap_cache_id;
> -    int64_t pixmap_cache_size;
> -    uint8_t pixmap_cache_freezer;
> -    uint64_t pixmap_cache_clients[MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS];
> -
> -    uint8_t glz_dict_id;
> -    GlzEncDictRestoreData glz_dict_data;
> -
> -    uint32_t surfaces_at_client_ptr; /* reference to MigrateDisplaySurfacesAtClientLossless/Lossy.
> -                                        Lossy: when jpeg-wan-compression(qemu cmd line)=always
> -                                        or when jpeg-wan-compression=auto,
> -                                        and low_bandwidth_setting=TRUE */
> -
> -} SpiceMigrateDataDisplay;
> -
> -typedef struct __attribute__ ((__packed__)) SpiceMigrateDataRect {
> -    int32_t left;
> -    int32_t top;
> -    int32_t right;
> -    int32_t bottom;
> -} SpiceMigrateDataRect;
> -
> -typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfaceLossless {
> -    uint32_t id;
> -} MigrateDisplaySurfaceLossless;
> -
> -typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfaceLossy {
> -    uint32_t id;
> -    SpiceMigrateDataRect lossy_rect;
> -} MigrateDisplaySurfaceLossy;
> -
> -typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfacesAtClientLossless {
> -    uint32_t num_surfaces;
> -    MigrateDisplaySurfaceLossless surfaces[0];
> -} MigrateDisplaySurfacesAtClientLossless;
> -
> -typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfacesAtClientLossy {
> -    uint32_t num_surfaces;
> -    MigrateDisplaySurfaceLossy surfaces[0];
> -} MigrateDisplaySurfacesAtClientLossy;
> -
> -/* ****************
> - * inputs channel
> - * ***************/
> -
> -#define SPICE_MIGRATE_DATA_INPUTS_VERSION 1
> -#define SPICE_MIGRATE_DATA_INPUTS_MAGIC SPICE_MAGIC_CONST("ICMD")
> -
> -
> -typedef struct __attribute__ ((__packed__)) SpiceMigrateDataInputs {
> -    uint16_t motion_count;
> -} SpiceMigrateDataInputs;
> -
> -static inline int migration_protocol_validate_header(SpiceMigrateDataHeader *header,
> -                                                     uint32_t magic,
> -                                                     uint32_t version)
> -{
> -    if (header->magic != magic) {
> -        spice_error("bad magic %u (!= %u)", header->magic, magic);
> -        return FALSE;
> -    }
> -    if (header->version > version) {
> -        spice_error("unsupported version %u (> %u)", header->version, version);
> -        return FALSE;
> -    }
> -    return TRUE;
> -}
> -
> -#endif
> diff --git a/server/mjpeg-encoder.c b/server/mjpeg-encoder.c
> new file mode 100644
> index 0000000..04c95a6
> --- /dev/null
> +++ b/server/mjpeg-encoder.c
> @@ -0,0 +1,1375 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include "red_common.h"
> +#include "mjpeg-encoder.h"
> +#include "utils.h"
> +#include <jerror.h>
> +#include <jpeglib.h>
> +#include <inttypes.h>
> +
> +#define MJPEG_MAX_FPS 25
> +#define MJPEG_MIN_FPS 1
> +
> +#define MJPEG_QUALITY_SAMPLE_NUM 7
> +static const int mjpeg_quality_samples[MJPEG_QUALITY_SAMPLE_NUM] = {20, 30, 40, 50, 60, 70, 80};
> +
> +#define MJPEG_LEGACY_STATIC_QUALITY_ID 5 // jpeg quality 70
> +
> +#define MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH 10
> +#define MJPEG_IMPROVE_QUALITY_FPS_PERMISSIVE_TH 5
> +
> +#define MJPEG_AVERAGE_SIZE_WINDOW 3
> +
> +#define MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES 3
> +#define MJPEG_LOW_FPS_RATE_TH 3
> +
> +#define MJPEG_SERVER_STATUS_EVAL_FPS_INTERVAL 1
> +#define MJPEG_SERVER_STATUS_DOWNGRADE_DROP_FACTOR_TH 0.1
> +
> +/*
> + * acting on positive client reports only if enough frame mm time
> + * has passed since the last bit rate change and the report.
> + * time
> + */
> +#define MJPEG_CLIENT_POSITIVE_REPORT_TIMEOUT 2000
> +#define MJPEG_CLIENT_POSITIVE_REPORT_STRICT_TIMEOUT 3000
> +
> +#define MJPEG_ADJUST_FPS_TIMEOUT 500
> +
> +/*
> + * avoid interrupting the playback when there are temporary
> + * incidents of instability (with respect to server and client drops)
> + */
> +#define MJPEG_MAX_CLIENT_PLAYBACK_DELAY 5000 // 5 sec
> +
> +/*
> + * The stream starts after lossless frames were sent to the client,
> + * and without rate control (except for pipe congestion). Thus, on the beginning
> + * of the stream, we might observe frame drops on the client and server side which
> + * are not necessarily related to mis-estimation of the bit rate, and we would
> + * like to wait till the stream stabilizes.
> + */
> +#define MJPEG_WARMUP_TIME 3000LL // 3 sec
> +
> +enum {
> +    MJPEG_QUALITY_EVAL_TYPE_SET,
> +    MJPEG_QUALITY_EVAL_TYPE_UPGRADE,
> +    MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE,
> +};
> +
> +enum {
> +    MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE,
> +    MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE,
> +};
> +
> +typedef struct MJpegEncoderQualityEval {
> +    int type;
> +    int reason;
> +
> +    uint64_t encoded_size_by_quality[MJPEG_QUALITY_SAMPLE_NUM];
> +    /* lower limit for the current evaluation round */
> +    int min_quality_id;
> +    int min_quality_fps; // min fps for the given quality
> +    /* upper limit for the current evaluation round */
> +    int max_quality_id;
> +    int max_quality_fps; // max fps for the given quality
> +    /* tracking the best sampled fps so far */
> +    int max_sampled_fps;
> +    int max_sampled_fps_quality_id;
> +} MJpegEncoderQualityEval;
> +
> +typedef struct MJpegEncoderClientState {
> +    int max_video_latency;
> +    uint32_t max_audio_latency;
> +} MJpegEncoderClientState;
> +
> +typedef struct MJpegEncoderServerState {
> +    uint32_t num_frames_encoded;
> +    uint32_t num_frames_dropped;
> +} MJpegEncoderServerState;
> +
> +typedef struct MJpegEncoderBitRateInfo {
> +    uint64_t change_start_time;
> +    uint64_t last_frame_time;
> +    uint32_t change_start_mm_time;
> +    int was_upgraded;
> +
> +    /* gathering data about the frames that
> +     * were encoded since the last bit rate change*/
> +    uint32_t num_enc_frames;
> +    uint64_t sum_enc_size;
> +} MJpegEncoderBitRateInfo;
> +
> +/*
> + * Adjusting the stream jpeg quality and frame rate (fps):
> + * When during_quality_eval=TRUE, we compress different frames with different
> + * jpeg quality. By considering (1) the resulting compression ratio, and (2) the available
> + * bit rate, we evaluate the max frame frequency for the stream with the given quality,
> + * and we choose the highest quality that will allow a reasonable frame rate.
> + * during_quality_eval is set for new streams and can also be set any time we want
> + * to re-evaluate the stream parameters (e.g., when the bit rate and/or
> + * compressed frame size significantly change).
> + */
> +typedef struct MJpegEncoderRateControl {
> +    int during_quality_eval;
> +    MJpegEncoderQualityEval quality_eval_data;
> +    MJpegEncoderBitRateInfo bit_rate_info;
> +    MJpegEncoderClientState client_state;
> +    MJpegEncoderServerState server_state;
> +
> +    uint64_t byte_rate;
> +    int quality_id;
> +    uint32_t fps;
> +    double adjusted_fps;
> +    uint64_t adjusted_fps_start_time;
> +    uint64_t adjusted_fps_num_frames;
> +
> +    /* the encoded frame size which the quality and the fps evaluation was based upon */
> +    uint64_t base_enc_size;
> +
> +    uint64_t last_enc_size;
> +
> +    uint64_t sum_recent_enc_size;
> +    uint32_t num_recent_enc_frames;
> +
> +    uint64_t warmup_start_time;
> +} MJpegEncoderRateControl;
> +
> +struct MJpegEncoder {
> +    uint8_t *row;
> +    uint32_t row_size;
> +    int first_frame;
> +
> +    struct jpeg_compress_struct cinfo;
> +    struct jpeg_error_mgr jerr;
> +
> +    unsigned int bytes_per_pixel; /* bytes per pixel of the input buffer */
> +    void (*pixel_converter)(void *src, uint8_t *dest);
> +
> +    MJpegEncoderRateControl rate_control;
> +    MJpegEncoderRateControlCbs cbs;
> +    void *cbs_opaque;
> +
> +    /* stats */
> +    uint64_t starting_bit_rate;
> +    uint64_t avg_quality;
> +    uint32_t num_frames;
> +};
> +
> +static void mjpeg_encoder_process_server_drops(MJpegEncoder *encoder);
> +static uint32_t get_min_required_playback_delay(uint64_t frame_enc_size,
> +                                                uint64_t byte_rate,
> +                                                uint32_t latency);
> +
> +static inline int rate_control_is_active(MJpegEncoder* encoder)
> +{
> +    return encoder->cbs.get_roundtrip_ms != NULL;
> +}
> +
> +void mjpeg_encoder_destroy(MJpegEncoder *encoder)
> +{
> +    free(encoder->cinfo.dest);
> +    jpeg_destroy_compress(&encoder->cinfo);
> +    free(encoder->row);
> +    free(encoder);
> +}
> +
> +static uint8_t mjpeg_encoder_get_bytes_per_pixel(MJpegEncoder *encoder)
> +{
> +    return encoder->bytes_per_pixel;
> +}
> +
> +#ifndef JCS_EXTENSIONS
> +/* Pixel conversion routines */
> +static void pixel_rgb24bpp_to_24(void *src_ptr, uint8_t *dest)
> +{
> +    uint8_t *src = src_ptr;
> +    /* libjpegs stores rgb, spice/win32 stores bgr */
> +    *dest++ = src[2]; /* red */
> +    *dest++ = src[1]; /* green */
> +    *dest++ = src[0]; /* blue */
> +}
> +
> +static void pixel_rgb32bpp_to_24(void *src, uint8_t *dest)
> +{
> +    uint32_t pixel = *(uint32_t *)src;
> +    *dest++ = (pixel >> 16) & 0xff;
> +    *dest++ = (pixel >>  8) & 0xff;
> +    *dest++ = (pixel >>  0) & 0xff;
> +}
> +#endif
> +
> +static void pixel_rgb16bpp_to_24(void *src, uint8_t *dest)
> +{
> +    uint16_t pixel = *(uint16_t *)src;
> +    *dest++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
> +    *dest++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
> +    *dest++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
> +}
> +
> +
> +/* code from libjpeg 8 to handle compression to a memory buffer
> + *
> + * Copyright (C) 1994-1996, Thomas G. Lane.
> + * Modified 2009 by Guido Vollbeding.
> + * This file is part of the Independent JPEG Group's software.
> + */
> +typedef struct {
> +  struct jpeg_destination_mgr pub; /* public fields */
> +
> +  unsigned char ** outbuffer;      /* target buffer */
> +  size_t * outsize;
> +  uint8_t * buffer;                /* start of buffer */
> +  size_t bufsize;
> +} mem_destination_mgr;
> +
> +static void init_mem_destination(j_compress_ptr cinfo)
> +{
> +}
> +
> +static boolean empty_mem_output_buffer(j_compress_ptr cinfo)
> +{
> +  size_t nextsize;
> +  uint8_t * nextbuffer;
> +  mem_destination_mgr *dest = (mem_destination_mgr *) cinfo->dest;
> +
> +  /* Try to allocate new buffer with double size */
> +  nextsize = dest->bufsize * 2;
> +  nextbuffer = malloc(nextsize);
> +
> +  if (nextbuffer == NULL)
> +    ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
> +
> +  memcpy(nextbuffer, dest->buffer, dest->bufsize);
> +
> +  free(dest->buffer);
> +
> +  dest->pub.next_output_byte = nextbuffer + dest->bufsize;
> +  dest->pub.free_in_buffer = dest->bufsize;
> +
> +  dest->buffer = nextbuffer;
> +  dest->bufsize = nextsize;
> +
> +  return TRUE;
> +}
> +
> +static void term_mem_destination(j_compress_ptr cinfo)
> +{
> +  mem_destination_mgr *dest = (mem_destination_mgr *) cinfo->dest;
> +
> +  *dest->outbuffer = dest->buffer;
> +  *dest->outsize = dest->bufsize;
> +}
> +
> +/*
> + * Prepare for output to a memory buffer.
> + * The caller may supply an own initial buffer with appropriate size.
> + * Otherwise, or when the actual data output exceeds the given size,
> + * the library adapts the buffer size as necessary.
> + * The standard library functions malloc/free are used for allocating
> + * larger memory, so the buffer is available to the application after
> + * finishing compression, and then the application is responsible for
> + * freeing the requested memory.
> + */
> +
> +static void
> +spice_jpeg_mem_dest(j_compress_ptr cinfo,
> +                    unsigned char ** outbuffer, size_t * outsize)
> +{
> +  mem_destination_mgr *dest;
> +#define OUTPUT_BUF_SIZE  4096 /* choose an efficiently fwrite'able size */
> +
> +  if (outbuffer == NULL || outsize == NULL) /* sanity check */
> +    ERREXIT(cinfo, JERR_BUFFER_SIZE);
> +
> +  /* The destination object is made permanent so that multiple JPEG images
> +   * can be written to the same buffer without re-executing jpeg_mem_dest.
> +   */
> +  if (cinfo->dest == NULL) { /* first time for this JPEG object? */
> +    cinfo->dest = spice_malloc(sizeof(mem_destination_mgr));
> +  }
> +
> +  dest = (mem_destination_mgr *) cinfo->dest;
> +  dest->pub.init_destination = init_mem_destination;
> +  dest->pub.empty_output_buffer = empty_mem_output_buffer;
> +  dest->pub.term_destination = term_mem_destination;
> +  dest->outbuffer = outbuffer;
> +  dest->outsize = outsize;
> +  if (*outbuffer == NULL || *outsize == 0) {
> +    /* Allocate initial buffer */
> +    *outbuffer = malloc(OUTPUT_BUF_SIZE);
> +    if (*outbuffer == NULL)
> +      ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
> +    *outsize = OUTPUT_BUF_SIZE;
> +  }
> +
> +  dest->pub.next_output_byte = dest->buffer = *outbuffer;
> +  dest->pub.free_in_buffer = dest->bufsize = *outsize;
> +}
> +/* end of code from libjpeg */
> +
> +static inline uint32_t mjpeg_encoder_get_source_fps(MJpegEncoder *encoder)
> +{
> +    return encoder->cbs.get_source_fps ?
> +        encoder->cbs.get_source_fps(encoder->cbs_opaque) : MJPEG_MAX_FPS;
> +}
> +
> +static inline uint32_t mjpeg_encoder_get_latency(MJpegEncoder *encoder)
> +{
> +    return encoder->cbs.get_roundtrip_ms ?
> +        encoder->cbs.get_roundtrip_ms(encoder->cbs_opaque) / 2 : 0;
> +}
> +
> +static uint32_t get_max_fps(uint64_t frame_size, uint64_t bytes_per_sec)
> +{
> +    double fps;
> +    double send_time_ms;
> +
> +    if (!bytes_per_sec) {
> +        return 0;
> +    }
> +    send_time_ms = frame_size * 1000.0 / bytes_per_sec;
> +    fps = send_time_ms ? 1000 / send_time_ms : MJPEG_MAX_FPS;
> +    return fps;
> +}
> +
> +static inline void mjpeg_encoder_reset_quality(MJpegEncoder *encoder,
> +                                               int quality_id,
> +                                               uint32_t fps,
> +                                               uint64_t frame_enc_size)
> +{
> +    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> +    double fps_ratio;
> +
> +    rate_control->during_quality_eval = FALSE;
> +
> +    if (rate_control->quality_id != quality_id) {
> +        rate_control->last_enc_size = 0;
> +    }
> +
> +    if (rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) {
> +        memset(&rate_control->server_state, 0, sizeof(MJpegEncoderServerState));
> +    }
> +    rate_control->quality_id = quality_id;
> +    memset(&rate_control->quality_eval_data, 0, sizeof(MJpegEncoderQualityEval));
> +    rate_control->quality_eval_data.max_quality_id = MJPEG_QUALITY_SAMPLE_NUM - 1;
> +    rate_control->quality_eval_data.max_quality_fps = MJPEG_MAX_FPS;
> +
> +    if (rate_control->adjusted_fps) {
> +        fps_ratio = rate_control->adjusted_fps / rate_control->fps;
> +    } else {
> +        fps_ratio = 1.5;
> +    }
> +    rate_control->fps = MAX(MJPEG_MIN_FPS, fps);
> +    rate_control->fps = MIN(MJPEG_MAX_FPS, rate_control->fps);
> +    rate_control->adjusted_fps = rate_control->fps*fps_ratio;
> +    spice_debug("adjusted-fps-ratio=%.2f adjusted-fps=%.2f", fps_ratio, rate_control->adjusted_fps);
> +    rate_control->adjusted_fps_start_time = 0;
> +    rate_control->adjusted_fps_num_frames = 0;
> +    rate_control->base_enc_size = frame_enc_size;
> +
> +    rate_control->sum_recent_enc_size = 0;
> +    rate_control->num_recent_enc_frames = 0;
> +}
> +
> +#define QUALITY_WAS_EVALUATED(encoder, quality) \
> +    ((encoder)->rate_control.quality_eval_data.encoded_size_by_quality[(quality)] != 0)
> +
> +/*
> + * Adjust the stream's jpeg quality and frame rate.
> + * We evaluate the compression ratio of different jpeg qualities;
> + * We compress successive frames with different qualities,
> + * and then we estimate the stream frame rate according to the currently
> + * evaluated jpeg quality and available bit rate.
> + *
> + * During quality evaluation, mjpeg_encoder_eval_quality is called before a new
> + * frame is encoded. mjpeg_encoder_eval_quality examines the encoding size of
> + * the previously encoded frame, and determines whether to continue evaluation
> + * (and chnages the quality for the frame that is going to be encoded),
> + * or stop evaluation (and sets the quality and frame rate for the stream).
> + * When qualities are scanned, we assume monotonicity of compression ratio
> + * as a function of jpeg quality. When we reach a quality with too small, or
> + * big enough compression ratio, we stop the evaluation and set the stream parameters.
> +*/
> +static inline void mjpeg_encoder_eval_quality(MJpegEncoder *encoder)
> +{
> +    MJpegEncoderRateControl *rate_control;
> +    MJpegEncoderQualityEval *quality_eval;
> +    uint32_t fps, src_fps;
> +    uint64_t enc_size;
> +    uint32_t final_quality_id;
> +    uint32_t final_fps;
> +    uint64_t final_quality_enc_size;
> +
> +    rate_control = &encoder->rate_control;
> +    quality_eval = &rate_control->quality_eval_data;
> +
> +    spice_assert(rate_control->during_quality_eval);
> +
> +    /* retrieving the encoded size of the last encoded frame */
> +    enc_size = quality_eval->encoded_size_by_quality[rate_control->quality_id];
> +    if (enc_size == 0) {
> +        spice_debug("size info missing");
> +        return;
> +    }
> +
> +    src_fps = mjpeg_encoder_get_source_fps(encoder);
> +
> +    fps = get_max_fps(enc_size, rate_control->byte_rate);
> +    spice_debug("mjpeg %p: jpeg %d: %.2f (KB) fps %d src-fps %u",
> +                encoder,
> +                mjpeg_quality_samples[rate_control->quality_id],
> +                enc_size / 1024.0,
> +                fps,
> +                src_fps);
> +
> +    if (fps > quality_eval->max_sampled_fps ||
> +        ((fps == quality_eval->max_sampled_fps || fps >= src_fps) &&
> +         rate_control->quality_id > quality_eval->max_sampled_fps_quality_id)) {
> +        quality_eval->max_sampled_fps = fps;
> +        quality_eval->max_sampled_fps_quality_id = rate_control->quality_id;
> +    }
> +
> +    /*
> +     * Choosing whether to evaluate another quality, or to complete evaluation
> +     * and set the stream parameters according to one of the qualities that
> +     * were already sampled.
> +     */
> +
> +    if (rate_control->quality_id > MJPEG_QUALITY_SAMPLE_NUM / 2 &&
> +        fps < MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH &&
> +        fps < src_fps) {
> +        /*
> +         * When the jpeg quality is bigger than the median quality, prefer a reasonable
> +         * frame rate over improving the quality
> +         */
> +        spice_debug("fps < %d && (fps < src_fps), quality %d",
> +                MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH,
> +                mjpeg_quality_samples[rate_control->quality_id]);
> +        if (QUALITY_WAS_EVALUATED(encoder, rate_control->quality_id - 1)) {
> +            /* the next worse quality was already evaluated and it passed the frame
> +             * rate thresholds (we know that, because we continued evaluating a better
> +             * quality) */
> +            rate_control->quality_id--;
> +            goto complete_sample;
> +        } else {
> +            /* evaluate the next worse quality */
> +            rate_control->quality_id--;
> +        }
> +    } else if ((fps > MJPEG_IMPROVE_QUALITY_FPS_PERMISSIVE_TH &&
> +                fps >= 0.66 * quality_eval->min_quality_fps) || fps >= src_fps) {
> +        /* When the jpeg quality is worse than the median one (see first condition), we allow a less
> +           strict threshold for fps, in order to improve the jpeg quality */
> +        if (rate_control->quality_id + 1 == MJPEG_QUALITY_SAMPLE_NUM ||
> +            rate_control->quality_id >= quality_eval->max_quality_id ||
> +            QUALITY_WAS_EVALUATED(encoder, rate_control->quality_id + 1)) {
> +            /* best quality has been reached, or the next (better) quality was
> +             * already evaluated and didn't pass the fps thresholds */
> +            goto complete_sample;
> +        } else {
> +            if (rate_control->quality_id == MJPEG_QUALITY_SAMPLE_NUM / 2 &&
> +                fps < MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH &&
> +                fps < src_fps) {
> +                goto complete_sample;
> +            }
> +            /* evaluate the next quality as well*/
> +            rate_control->quality_id++;
> +        }
> +    } else { // very small frame rate, try to improve by downgrading the quality
> +        if (rate_control->quality_id == 0 ||
> +            rate_control->quality_id <= quality_eval->min_quality_id) {
> +            goto complete_sample;
> +        } else if (QUALITY_WAS_EVALUATED(encoder, rate_control->quality_id - 1)) {
> +            rate_control->quality_id--;
> +            goto complete_sample;
> +        } else {
> +            /* evaluate the next worse quality */
> +            rate_control->quality_id--;
> +        }
> +    }
> +    return;
> +
> +complete_sample:
> +    if (quality_eval->max_sampled_fps != 0) {
> +        /* covering a case were monotonicity was violated and we sampled
> +           a better jepg quality, with better frame rate. */
> +        final_quality_id = MAX(rate_control->quality_id,
> +                               quality_eval->max_sampled_fps_quality_id);
> +    } else {
> +        final_quality_id = rate_control->quality_id;
> +    }
> +    final_quality_enc_size = quality_eval->encoded_size_by_quality[final_quality_id];
> +    final_fps = get_max_fps(final_quality_enc_size,
> +                            rate_control->byte_rate);
> +
> +    if (final_quality_id == quality_eval->min_quality_id) {
> +        final_fps = MAX(final_fps, quality_eval->min_quality_fps);
> +    }
> +    if (final_quality_id == quality_eval->max_quality_id) {
> +        final_fps = MIN(final_fps, quality_eval->max_quality_fps);
> +    }
> +    mjpeg_encoder_reset_quality(encoder, final_quality_id, final_fps, final_quality_enc_size);
> +    rate_control->sum_recent_enc_size = final_quality_enc_size;
> +    rate_control->num_recent_enc_frames = 1;
> +
> +    spice_debug("MJpeg quality sample end %p: quality %d fps %d",
> +                encoder, mjpeg_quality_samples[rate_control->quality_id], rate_control->fps);
> +    if (encoder->cbs.update_client_playback_delay) {
> +        uint32_t latency = mjpeg_encoder_get_latency(encoder);
> +        uint32_t min_delay = get_min_required_playback_delay(final_quality_enc_size,
> +                                                             rate_control->byte_rate,
> +                                                             latency);
> +
> +        encoder->cbs.update_client_playback_delay(encoder->cbs_opaque, min_delay);
> +    }
> +}
> +
> +static void mjpeg_encoder_quality_eval_set_upgrade(MJpegEncoder *encoder,
> +                                                   int reason,
> +                                                   uint32_t min_quality_id,
> +                                                   uint32_t min_quality_fps)
> +{
> +    MJpegEncoderQualityEval *quality_eval = &encoder->rate_control.quality_eval_data;
> +
> +    encoder->rate_control.during_quality_eval = TRUE;
> +    quality_eval->type = MJPEG_QUALITY_EVAL_TYPE_UPGRADE;
> +    quality_eval->reason = reason;
> +    quality_eval->min_quality_id = min_quality_id;
> +    quality_eval->min_quality_fps = min_quality_fps;
> +}
> +
> +static void mjpeg_encoder_quality_eval_set_downgrade(MJpegEncoder *encoder,
> +                                                     int reason,
> +                                                     uint32_t max_quality_id,
> +                                                     uint32_t max_quality_fps)
> +{
> +    MJpegEncoderQualityEval *quality_eval = &encoder->rate_control.quality_eval_data;
> +
> +    encoder->rate_control.during_quality_eval = TRUE;
> +    quality_eval->type = MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE;
> +    quality_eval->reason = reason;
> +    quality_eval->max_quality_id = max_quality_id;
> +    quality_eval->max_quality_fps = max_quality_fps;
> +}
> +
> +static void mjpeg_encoder_adjust_params_to_bit_rate(MJpegEncoder *encoder)
> +{
> +    MJpegEncoderRateControl *rate_control;
> +    MJpegEncoderQualityEval *quality_eval;
> +    uint64_t new_avg_enc_size = 0;
> +    uint32_t new_fps;
> +    uint32_t latency = 0;
> +    uint32_t src_fps;
> +
> +    spice_assert(rate_control_is_active(encoder));
> +
> +    rate_control = &encoder->rate_control;
> +    quality_eval = &rate_control->quality_eval_data;
> +
> +    if (!rate_control->last_enc_size) {
> +        spice_debug("missing sample size");
> +        return;
> +    }
> +
> +    if (rate_control->during_quality_eval) {
> +        quality_eval->encoded_size_by_quality[rate_control->quality_id] = rate_control->last_enc_size;
> +        mjpeg_encoder_eval_quality(encoder);
> +        return;
> +    }
> +
> +    if (!rate_control->num_recent_enc_frames) {
> +        spice_debug("No recent encoded frames");
> +        return;
> +    }
> +
> +    if (rate_control->num_recent_enc_frames < MJPEG_AVERAGE_SIZE_WINDOW &&
> +        rate_control->num_recent_enc_frames < rate_control->fps) {
> +        goto end;
> +    }
> +
> +    latency = mjpeg_encoder_get_latency(encoder);
> +    new_avg_enc_size = rate_control->sum_recent_enc_size /
> +                       rate_control->num_recent_enc_frames;
> +    new_fps = get_max_fps(new_avg_enc_size, rate_control->byte_rate);
> +
> +    spice_debug("cur-fps=%u new-fps=%u (new/old=%.2f) |"
> +                "bit-rate=%.2f (Mbps) latency=%u (ms) quality=%d |"
> +                " new-size-avg %"PRIu64" , base-size %"PRIu64", (new/old=%.2f) ",
> +                rate_control->fps, new_fps, ((double)new_fps)/rate_control->fps,
> +                ((double)rate_control->byte_rate*8)/1024/1024,
> +                latency,
> +                mjpeg_quality_samples[rate_control->quality_id],
> +                new_avg_enc_size, rate_control->base_enc_size,
> +                rate_control->base_enc_size ?
> +                    ((double)new_avg_enc_size) / rate_control->base_enc_size :
> +                    1);
> +
> +     src_fps = mjpeg_encoder_get_source_fps(encoder);
> +
> +    /*
> +     * The ratio between the new_fps and the current fps reflects the changes
> +     * in latency and frame size. When the change passes a threshold,
> +     * we re-evaluate the quality and frame rate.
> +     */
> +    if (new_fps > rate_control->fps &&
> +        (rate_control->fps < src_fps || rate_control->quality_id < MJPEG_QUALITY_SAMPLE_NUM - 1)) {
> +        spice_debug("mjpeg %p FPS CHANGE >> :  re-evaluating params", encoder);
> +        mjpeg_encoder_quality_eval_set_upgrade(encoder, MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE,
> +                                               rate_control->quality_id, /* fps has improved -->
> +                                                                            don't allow stream quality
> +                                                                            to deteriorate */
> +                                               rate_control->fps);
> +    } else if (new_fps < rate_control->fps && new_fps < src_fps) {
> +        spice_debug("mjpeg %p FPS CHANGE << : re-evaluating params", encoder);
> +        mjpeg_encoder_quality_eval_set_downgrade(encoder, MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE,
> +                                                 rate_control->quality_id,
> +                                                 rate_control->fps);
> +    }
> +end:
> +    if (rate_control->during_quality_eval) {
> +        quality_eval->encoded_size_by_quality[rate_control->quality_id] = new_avg_enc_size;
> +        mjpeg_encoder_eval_quality(encoder);
> +    } else {
> +        mjpeg_encoder_process_server_drops(encoder);
> +    }
> +}
> +
> +/*
> + * The actual frames distribution does not necessarily fit the condition "at least
> + * one frame every (1000/rate_contorl->fps) milliseconds".
> + * For keeping the average fps close to the defined fps, we periodically
> + * measure the current average fps, and modify rate_control->adjusted_fps accordingly.
> + * Then, we use (1000/rate_control->adjusted_fps) as the interval between frames.
> + */
> +static void mjpeg_encoder_adjust_fps(MJpegEncoder *encoder, uint64_t now)
> +{
> +    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> +    uint64_t adjusted_fps_time_passed;
> +
> +    spice_assert(rate_control_is_active(encoder));
> +
> +    adjusted_fps_time_passed = (now - rate_control->adjusted_fps_start_time) / 1000 / 1000;
> +
> +    if (!rate_control->during_quality_eval &&
> +        adjusted_fps_time_passed > MJPEG_ADJUST_FPS_TIMEOUT &&
> +        adjusted_fps_time_passed > 1000 / rate_control->adjusted_fps) {
> +        double avg_fps;
> +        double fps_ratio;
> +
> +        avg_fps = ((double)rate_control->adjusted_fps_num_frames*1000) /
> +                  adjusted_fps_time_passed;
> +        spice_debug("#frames-adjust=%"PRIu64" #adjust-time=%"PRIu64" avg-fps=%.2f",
> +                    rate_control->adjusted_fps_num_frames, adjusted_fps_time_passed, avg_fps);
> +        spice_debug("defined=%u old-adjusted=%.2f", rate_control->fps, rate_control->adjusted_fps);
> +        fps_ratio = avg_fps / rate_control->fps;
> +        if (avg_fps + 0.5 < rate_control->fps &&
> +            mjpeg_encoder_get_source_fps(encoder) > avg_fps) {
> +            double new_adjusted_fps = avg_fps ?
> +                                               (rate_control->adjusted_fps/fps_ratio) :
> +                                               rate_control->adjusted_fps * 2;
> +
> +            rate_control->adjusted_fps = MIN(rate_control->fps*2, new_adjusted_fps);
> +            spice_debug("new-adjusted-fps=%.2f", rate_control->adjusted_fps);
> +        } else if (rate_control->fps + 0.5 < avg_fps) {
> +            double new_adjusted_fps = rate_control->adjusted_fps / fps_ratio;
> +
> +            rate_control->adjusted_fps = MAX(rate_control->fps, new_adjusted_fps);
> +            spice_debug("new-adjusted-fps=%.2f", rate_control->adjusted_fps);
> +        }
> +        rate_control->adjusted_fps_start_time = now;
> +        rate_control->adjusted_fps_num_frames = 0;
> +    }
> +}
> +
> +/*
> + * dest must be either NULL or allocated by malloc, since it might be freed
> + * during the encoding, if its size is too small.
> + *
> + * return:
> + *  MJPEG_ENCODER_FRAME_UNSUPPORTED : frame cannot be encoded
> + *  MJPEG_ENCODER_FRAME_DROP        : frame should be dropped. This value can only be returned
> + *                                    if mjpeg rate control is active.
> + *  MJPEG_ENCODER_FRAME_ENCODE_DONE : frame encoding started. Continue with
> + *                                    mjpeg_encoder_encode_scanline.
> + */
> +static int mjpeg_encoder_start_frame(MJpegEncoder *encoder,
> +                                     SpiceBitmapFmt format,
> +                                     int width, int height,
> +                                     uint8_t **dest, size_t *dest_len,
> +                                     uint32_t frame_mm_time)
> +{
> +    uint32_t quality;
> +
> +    if (rate_control_is_active(encoder)) {
> +        MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> +        uint64_t now;
> +        uint64_t interval;
> +
> +        now = red_get_monotonic_time();
> +
> +        if (!rate_control->adjusted_fps_start_time) {
> +            rate_control->adjusted_fps_start_time = now;
> +        }
> +        mjpeg_encoder_adjust_fps(encoder, now);
> +        interval = (now - rate_control->bit_rate_info.last_frame_time);
> +
> +        if (interval < (1000*1000*1000) / rate_control->adjusted_fps) {
> +            return MJPEG_ENCODER_FRAME_DROP;
> +        }
> +
> +        mjpeg_encoder_adjust_params_to_bit_rate(encoder);
> +
> +        if (!rate_control->during_quality_eval ||
> +            rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE) {
> +            MJpegEncoderBitRateInfo *bit_rate_info;
> +
> +            bit_rate_info = &encoder->rate_control.bit_rate_info;
> +
> +            if (!bit_rate_info->change_start_time) {
> +                bit_rate_info->change_start_time = now;
> +                bit_rate_info->change_start_mm_time = frame_mm_time;
> +            }
> +            bit_rate_info->last_frame_time = now;
> +        }
> +    }
> +
> +    encoder->cinfo.in_color_space   = JCS_RGB;
> +    encoder->cinfo.input_components = 3;
> +    encoder->pixel_converter = NULL;
> +
> +    switch (format) {
> +    case SPICE_BITMAP_FMT_32BIT:
> +    case SPICE_BITMAP_FMT_RGBA:
> +        encoder->bytes_per_pixel = 4;
> +#ifdef JCS_EXTENSIONS
> +        encoder->cinfo.in_color_space   = JCS_EXT_BGRX;
> +        encoder->cinfo.input_components = 4;
> +#else
> +        encoder->pixel_converter = pixel_rgb32bpp_to_24;
> +#endif
> +        break;
> +    case SPICE_BITMAP_FMT_16BIT:
> +        encoder->bytes_per_pixel = 2;
> +        encoder->pixel_converter = pixel_rgb16bpp_to_24;
> +        break;
> +    case SPICE_BITMAP_FMT_24BIT:
> +        encoder->bytes_per_pixel = 3;
> +#ifdef JCS_EXTENSIONS
> +        encoder->cinfo.in_color_space = JCS_EXT_BGR;
> +#else
> +        encoder->pixel_converter = pixel_rgb24bpp_to_24;
> +#endif
> +        break;
> +    default:
> +        spice_debug("unsupported format %d", format);
> +        return MJPEG_ENCODER_FRAME_UNSUPPORTED;
> +    }
> +
> +    if (encoder->pixel_converter != NULL) {
> +        unsigned int stride = width * 3;
> +        /* check for integer overflow */
> +        if (stride < width) {
> +            return MJPEG_ENCODER_FRAME_UNSUPPORTED;
> +        }
> +        if (encoder->row_size < stride) {
> +            encoder->row = spice_realloc(encoder->row, stride);
> +            encoder->row_size = stride;
> +        }
> +    }
> +
> +    spice_jpeg_mem_dest(&encoder->cinfo, dest, dest_len);
> +
> +    encoder->cinfo.image_width      = width;
> +    encoder->cinfo.image_height     = height;
> +    jpeg_set_defaults(&encoder->cinfo);
> +    encoder->cinfo.dct_method       = JDCT_IFAST;
> +    quality = mjpeg_quality_samples[encoder->rate_control.quality_id];
> +    jpeg_set_quality(&encoder->cinfo, quality, TRUE);
> +    jpeg_start_compress(&encoder->cinfo, encoder->first_frame);
> +
> +    encoder->num_frames++;
> +    encoder->avg_quality += quality;
> +    return MJPEG_ENCODER_FRAME_ENCODE_DONE;
> +}
> +
> +static int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder,
> +                                         uint8_t *src_pixels,
> +                                         size_t image_width)
> +{
> +    unsigned int scanlines_written;
> +    uint8_t *row;
> +
> +    row = encoder->row;
> +    if (encoder->pixel_converter) {
> +        unsigned int x;
> +        for (x = 0; x < image_width; x++) {
> +            /* src_pixels is expected to be 4 bytes aligned */
> +            encoder->pixel_converter(src_pixels, row);
> +            row += 3;
> +            src_pixels += encoder->bytes_per_pixel;
> +        }
> +        scanlines_written = jpeg_write_scanlines(&encoder->cinfo, &encoder->row, 1);
> +    } else {
> +        scanlines_written = jpeg_write_scanlines(&encoder->cinfo, &src_pixels, 1);
> +    }
> +    if (scanlines_written == 0) { /* Not enough space */
> +        jpeg_abort_compress(&encoder->cinfo);
> +        encoder->rate_control.last_enc_size = 0;
> +        return 0;
> +    }
> +
> +    return scanlines_written;
> +}
> +
> +static size_t mjpeg_encoder_end_frame(MJpegEncoder *encoder)
> +{
> +    mem_destination_mgr *dest = (mem_destination_mgr *) encoder->cinfo.dest;
> +    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> +
> +    jpeg_finish_compress(&encoder->cinfo);
> +
> +    encoder->first_frame = FALSE;
> +    rate_control->last_enc_size = dest->pub.next_output_byte - dest->buffer;
> +    rate_control->server_state.num_frames_encoded++;
> +
> +    if (!rate_control->during_quality_eval ||
> +        rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE) {
> +
> +        if (!rate_control->during_quality_eval) {
> +            if (rate_control->num_recent_enc_frames >= MJPEG_AVERAGE_SIZE_WINDOW) {
> +                rate_control->num_recent_enc_frames = 0;
> +                rate_control->sum_recent_enc_size = 0;
> +            }
> +            rate_control->sum_recent_enc_size += rate_control->last_enc_size;
> +            rate_control->num_recent_enc_frames++;
> +            rate_control->adjusted_fps_num_frames++;
> +        }
> +        rate_control->bit_rate_info.sum_enc_size += encoder->rate_control.last_enc_size;
> +        rate_control->bit_rate_info.num_enc_frames++;
> +    }
> +    return encoder->rate_control.last_enc_size;
> +}
> +
> +static inline uint8_t *get_image_line(SpiceChunks *chunks, size_t *offset,
> +                                      int *chunk_nr, int stride)
> +{
> +    uint8_t *ret;
> +    SpiceChunk *chunk;
> +
> +    chunk = &chunks->chunk[*chunk_nr];
> +
> +    if (*offset == chunk->len) {
> +        if (*chunk_nr == chunks->num_chunks - 1) {
> +            return NULL; /* Last chunk */
> +        }
> +        *offset = 0;
> +        (*chunk_nr)++;
> +        chunk = &chunks->chunk[*chunk_nr];
> +    }
> +
> +    if (chunk->len - *offset < stride) {
> +        spice_warning("bad chunk alignment");
> +        return NULL;
> +    }
> +    ret = chunk->data + *offset;
> +    *offset += stride;
> +    return ret;
> +}
> +
> +static int encode_frame(MJpegEncoder *encoder, const SpiceRect *src,
> +                        const SpiceBitmap *image, int top_down)
> +{
> +    SpiceChunks *chunks;
> +    uint32_t image_stride;
> +    size_t offset;
> +    int i, chunk;
> +
> +    chunks = image->data;
> +    offset = 0;
> +    chunk = 0;
> +    image_stride = image->stride;
> +
> +    const int skip_lines = top_down ? src->top : image->y - (src->bottom - 0);
> +    for (i = 0; i < skip_lines; i++) {
> +        get_image_line(chunks, &offset, &chunk, image_stride);
> +    }
> +
> +    const unsigned int stream_height = src->bottom - src->top;
> +    const unsigned int stream_width = src->right - src->left;
> +
> +    for (i = 0; i < stream_height; i++) {
> +        uint8_t *src_line = get_image_line(chunks, &offset, &chunk, image_stride);
> +
> +        if (!src_line) {
> +            return FALSE;
> +        }
> +
> +        src_line += src->left * mjpeg_encoder_get_bytes_per_pixel(encoder);
> +        if (mjpeg_encoder_encode_scanline(encoder, src_line, stream_width) == 0) {
> +            return FALSE;
> +        }
> +    }
> +
> +    return TRUE;
> +}
> +
> +int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
> +                               const SpiceBitmap *bitmap, int width, int height,
> +                               const SpiceRect *src,
> +                               int top_down, uint32_t frame_mm_time,
> +                               uint8_t **outbuf, size_t *outbuf_size,
> +                               int *data_size)
> +{
> +    int ret = mjpeg_encoder_start_frame(encoder, bitmap->format,
> +                                    width, height, outbuf, outbuf_size,
> +                                    frame_mm_time);
> +    if (ret != MJPEG_ENCODER_FRAME_ENCODE_DONE) {
> +        return ret;
> +    }
> +
> +    if (!encode_frame(encoder, src, bitmap, top_down)) {
> +        return MJPEG_ENCODER_FRAME_UNSUPPORTED;
> +    }
> +
> +    *data_size = mjpeg_encoder_end_frame(encoder);
> +
> +    return MJPEG_ENCODER_FRAME_ENCODE_DONE;
> +}
> +
> +
> +static void mjpeg_encoder_quality_eval_stop(MJpegEncoder *encoder)
> +{
> +    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> +    uint32_t quality_id;
> +    uint32_t fps;
> +
> +    if (!rate_control->during_quality_eval) {
> +        return;
> +    }
> +    switch (rate_control->quality_eval_data.type) {
> +    case MJPEG_QUALITY_EVAL_TYPE_UPGRADE:
> +        quality_id = rate_control->quality_eval_data.min_quality_id;
> +        fps = rate_control->quality_eval_data.min_quality_fps;
> +        break;
> +    case MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE:
> +        quality_id = rate_control->quality_eval_data.max_quality_id;
> +        fps = rate_control->quality_eval_data.max_quality_fps;
> +        break;
> +    case MJPEG_QUALITY_EVAL_TYPE_SET:
> +        quality_id = MJPEG_QUALITY_SAMPLE_NUM / 2;
> +        fps = MJPEG_MAX_FPS / 2;
> +        break;
> +    default:
> +        spice_warning("unexected");
> +        return;
> +    }
> +    mjpeg_encoder_reset_quality(encoder, quality_id, fps, 0);
> +    spice_debug("during quality evaluation: canceling."
> +                "reset quality to %d fps %d",
> +                mjpeg_quality_samples[rate_control->quality_id], rate_control->fps);
> +}
> +
> +static void mjpeg_encoder_decrease_bit_rate(MJpegEncoder *encoder)
> +{
> +    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> +    MJpegEncoderBitRateInfo *bit_rate_info = &rate_control->bit_rate_info;
> +    uint64_t measured_byte_rate;
> +    uint32_t measured_fps;
> +    uint64_t decrease_size;
> +
> +    mjpeg_encoder_quality_eval_stop(encoder);
> +
> +    rate_control->client_state.max_video_latency = 0;
> +    rate_control->client_state.max_audio_latency = 0;
> +    if (rate_control->warmup_start_time) {
> +        uint64_t now;
> +
> +        now = red_get_monotonic_time();
> +        if (now - rate_control->warmup_start_time < MJPEG_WARMUP_TIME*1000*1000) {
> +            spice_debug("during warmup. ignoring");
> +            return;
> +        } else {
> +            rate_control->warmup_start_time = 0;
> +        }
> +    }
> +
> +    if (bit_rate_info->num_enc_frames > MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES ||
> +        bit_rate_info->num_enc_frames > rate_control->fps) {
> +        double duration_sec;
> +
> +        duration_sec = (bit_rate_info->last_frame_time - bit_rate_info->change_start_time);
> +        duration_sec /= (1000.0 * 1000.0 * 1000.0);
> +        measured_byte_rate = bit_rate_info->sum_enc_size / duration_sec;
> +        measured_fps = bit_rate_info->num_enc_frames / duration_sec;
> +        decrease_size = bit_rate_info->sum_enc_size / bit_rate_info->num_enc_frames;
> +        spice_debug("bit rate esitimation %.2f (Mbps) fps %u",
> +                    measured_byte_rate*8/1024.0/1024,
> +                    measured_fps);
> +    } else {
> +        measured_byte_rate = rate_control->byte_rate;
> +        measured_fps = rate_control->fps;
> +        decrease_size = measured_byte_rate/measured_fps;
> +        spice_debug("bit rate not re-estimated %.2f (Mbps) fps %u",
> +                    measured_byte_rate*8/1024.0/1024,
> +                    measured_fps);
> +    }
> +
> +    measured_byte_rate = MIN(rate_control->byte_rate, measured_byte_rate);
> +
> +    if (decrease_size >=  measured_byte_rate) {
> +        decrease_size = measured_byte_rate / 2;
> +    }
> +
> +    rate_control->byte_rate = measured_byte_rate - decrease_size;
> +    bit_rate_info->change_start_time = 0;
> +    bit_rate_info->change_start_mm_time = 0;
> +    bit_rate_info->last_frame_time = 0;
> +    bit_rate_info->num_enc_frames = 0;
> +    bit_rate_info->sum_enc_size = 0;
> +    bit_rate_info->was_upgraded = FALSE;
> +
> +    spice_debug("decrease bit rate %.2f (Mbps)", rate_control->byte_rate * 8 / 1024.0/1024.0);
> +    mjpeg_encoder_quality_eval_set_downgrade(encoder,
> +                                             MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE,
> +                                             rate_control->quality_id,
> +                                             rate_control->fps);
> +}
> +
> +static void mjpeg_encoder_handle_negative_client_stream_report(MJpegEncoder *encoder,
> +                                                               uint32_t report_end_frame_mm_time)
> +{
> +    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> +
> +    spice_debug(NULL);
> +
> +    if ((rate_control->bit_rate_info.change_start_mm_time > report_end_frame_mm_time ||
> +        !rate_control->bit_rate_info.change_start_mm_time) &&
> +         !rate_control->bit_rate_info.was_upgraded) {
> +        spice_debug("ignoring, a downgrade has already occurred later to the report time");
> +        return;
> +    }
> +
> +    mjpeg_encoder_decrease_bit_rate(encoder);
> +}
> +
> +static void mjpeg_encoder_increase_bit_rate(MJpegEncoder *encoder)
> +{
> +    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> +    MJpegEncoderBitRateInfo *bit_rate_info = &rate_control->bit_rate_info;
> +    uint64_t measured_byte_rate;
> +    uint32_t measured_fps;
> +    uint64_t increase_size;
> +
> +
> +    if (bit_rate_info->num_enc_frames > MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES ||
> +        bit_rate_info->num_enc_frames > rate_control->fps) {
> +        uint64_t avg_frame_size;
> +        double duration_sec;
> +
> +        duration_sec = (bit_rate_info->last_frame_time - bit_rate_info->change_start_time);
> +        duration_sec /= (1000.0 * 1000.0 * 1000.0);
> +        measured_byte_rate = bit_rate_info->sum_enc_size / duration_sec;
> +        measured_fps = bit_rate_info->num_enc_frames / duration_sec;
> +        avg_frame_size = bit_rate_info->sum_enc_size / bit_rate_info->num_enc_frames;
> +        spice_debug("bit rate esitimation %.2f (Mbps) defined %.2f"
> +                    " fps %u avg-frame-size=%.2f (KB)",
> +                    measured_byte_rate*8/1024.0/1024,
> +                    rate_control->byte_rate*8/1024.0/1024,
> +                    measured_fps,
> +                    avg_frame_size/1024.0);
> +        increase_size = avg_frame_size;
> +    } else {
> +        spice_debug("not enough samples for measuring the bit rate. no change");
> +        return;
> +    }
> +
> +
> +    mjpeg_encoder_quality_eval_stop(encoder);
> +
> +    if (measured_byte_rate + increase_size < rate_control->byte_rate) {
> +        spice_debug("measured byte rate is small: not upgrading, just re-evaluating");
> +    } else {
> +        rate_control->byte_rate = MIN(measured_byte_rate, rate_control->byte_rate) + increase_size;
> +    }
> +
> +    bit_rate_info->change_start_time = 0;
> +    bit_rate_info->change_start_mm_time = 0;
> +    bit_rate_info->last_frame_time = 0;
> +    bit_rate_info->num_enc_frames = 0;
> +    bit_rate_info->sum_enc_size = 0;
> +    bit_rate_info->was_upgraded = TRUE;
> +
> +    spice_debug("increase bit rate %.2f (Mbps)", rate_control->byte_rate * 8 / 1024.0/1024.0);
> +    mjpeg_encoder_quality_eval_set_upgrade(encoder,
> +                                           MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE,
> +                                           rate_control->quality_id,
> +                                           rate_control->fps);
> +}
> +
> +static void mjpeg_encoder_handle_positive_client_stream_report(MJpegEncoder *encoder,
> +                                                               uint32_t report_start_frame_mm_time)
> +{
> +    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> +    MJpegEncoderBitRateInfo *bit_rate_info = &rate_control->bit_rate_info;
> +    int stable_client_mm_time;
> +    int timeout;
> +
> +    if (rate_control->during_quality_eval &&
> +        rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) {
> +        spice_debug("during quality evaluation (rate change). ignoring report");
> +        return;
> +    }
> +
> +    if ((rate_control->fps > MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH ||
> +         rate_control->fps >= mjpeg_encoder_get_source_fps(encoder)) &&
> +         rate_control->quality_id > MJPEG_QUALITY_SAMPLE_NUM / 2) {
> +        timeout = MJPEG_CLIENT_POSITIVE_REPORT_STRICT_TIMEOUT;
> +    } else {
> +        timeout = MJPEG_CLIENT_POSITIVE_REPORT_TIMEOUT;
> +    }
> +
> +    stable_client_mm_time = (int)report_start_frame_mm_time - bit_rate_info->change_start_mm_time;
> +
> +    if (!bit_rate_info->change_start_mm_time || stable_client_mm_time < timeout) {
> +        /* assessing the stability of the current setting and only then
> +         * respond to the report */
> +        spice_debug("no drops, but not enough time has passed for assessing"
> +                    "the playback stability since the last bit rate change");
> +        return;
> +    }
> +    mjpeg_encoder_increase_bit_rate(encoder);
> +}
> +
> +/*
> + * the video playback jitter buffer should be at least (send_time*2 + net_latency) for
> + * preventing underflow
> + */
> +static uint32_t get_min_required_playback_delay(uint64_t frame_enc_size,
> +                                                uint64_t byte_rate,
> +                                                uint32_t latency)
> +{
> +    uint32_t one_frame_time;
> +    uint32_t min_delay;
> +
> +    if (!frame_enc_size || !byte_rate) {
> +        return latency;
> +    }
> +    one_frame_time = (frame_enc_size*1000)/byte_rate;
> +
> +    min_delay = MIN(one_frame_time*2 + latency, MJPEG_MAX_CLIENT_PLAYBACK_DELAY);
> +    return min_delay;
> +}
> +
> +#define MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR 0.5
> +#define MJPEG_VIDEO_VS_AUDIO_LATENCY_FACTOR 1.25
> +#define MJPEG_VIDEO_DELAY_TH -15
> +
> +void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,
> +                                        uint32_t num_frames,
> +                                        uint32_t num_drops,
> +                                        uint32_t start_frame_mm_time,
> +                                        uint32_t end_frame_mm_time,
> +                                        int32_t end_frame_delay,
> +                                        uint32_t audio_delay)
> +{
> +    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> +    MJpegEncoderClientState *client_state = &rate_control->client_state;
> +    uint64_t avg_enc_size = 0;
> +    uint32_t min_playback_delay;
> +    int is_video_delay_small = FALSE;
> +
> +    spice_debug("client report: #frames %u, #drops %d, duration %u video-delay %d audio-delay %u",
> +                num_frames, num_drops,
> +                end_frame_mm_time - start_frame_mm_time,
> +                end_frame_delay, audio_delay);
> +
> +    if (!rate_control_is_active(encoder)) {
> +        spice_debug("rate control was not activated: ignoring");
> +        return;
> +    }
> +    if (rate_control->during_quality_eval) {
> +        if (rate_control->quality_eval_data.type == MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE &&
> +            rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) {
> +            spice_debug("during rate downgrade evaluation");
> +            return;
> +        }
> +    }
> +
> +    if (rate_control->num_recent_enc_frames) {
> +        avg_enc_size = rate_control->sum_recent_enc_size /
> +                       rate_control->num_recent_enc_frames;
> +    }
> +    spice_debug("recent size avg %.2f (KB)", avg_enc_size / 1024.0);
> +    min_playback_delay = get_min_required_playback_delay(avg_enc_size, rate_control->byte_rate,
> +                                                         mjpeg_encoder_get_latency(encoder));
> +    spice_debug("min-delay %u client-delay %d", min_playback_delay, end_frame_delay);
> +
> +    if (min_playback_delay > end_frame_delay) {
> +        uint32_t src_fps = mjpeg_encoder_get_source_fps(encoder);
> +        /*
> +        * if the stream is at its highest rate, we can't estimate the "real"
> +        * network bit rate and the min_playback_delay
> +        */
> +        if (rate_control->quality_id != MJPEG_QUALITY_SAMPLE_NUM - 1 ||
> +            rate_control->fps < MIN(src_fps, MJPEG_MAX_FPS) || end_frame_delay < 0) {
> +            is_video_delay_small = TRUE;
> +            if (encoder->cbs.update_client_playback_delay) {
> +                encoder->cbs.update_client_playback_delay(encoder->cbs_opaque,
> +                                                          min_playback_delay);
> +            }
> +        }
> +    }
> +
> +
> +    /*
> +     * If the audio latency has decreased (since the start of the current
> +     * sequence of positive reports), and the video latency is bigger, slow down
> +     * the video rate
> +     */
> +    if (end_frame_delay > 0 &&
> +        audio_delay < MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR*client_state->max_audio_latency &&
> +        end_frame_delay > MJPEG_VIDEO_VS_AUDIO_LATENCY_FACTOR*audio_delay) {
> +        spice_debug("video_latency >> audio_latency && audio_latency << max (%u)",
> +                    client_state->max_audio_latency);
> +        mjpeg_encoder_handle_negative_client_stream_report(encoder,
> +                                                           end_frame_mm_time);
> +        return;
> +    }
> +
> +    if (end_frame_delay < MJPEG_VIDEO_DELAY_TH) {
> +        mjpeg_encoder_handle_negative_client_stream_report(encoder,
> +                                                           end_frame_mm_time);
> +    } else {
> +        double major_delay_decrease_thresh;
> +        double medium_delay_decrease_thresh;
> +
> +        client_state->max_video_latency = MAX(end_frame_delay, client_state->max_video_latency);
> +        client_state->max_audio_latency = MAX(audio_delay, client_state->max_audio_latency);
> +
> +        medium_delay_decrease_thresh = client_state->max_video_latency;
> +        medium_delay_decrease_thresh *= MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR;
> +
> +        major_delay_decrease_thresh = medium_delay_decrease_thresh;
> +        major_delay_decrease_thresh *= MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR;
> +        /*
> +         * since the bit rate and the required latency are only evaluation based on the
> +         * reports we got till now, we assume that the latency is too low only if it
> +         * was higher during the time that passed since the last report that resulted
> +         * in a bit rate decrement. If we find that the latency has decreased, it might
> +         * suggest that the stream bit rate is too high.
> +         */
> +        if ((end_frame_delay < medium_delay_decrease_thresh &&
> +            is_video_delay_small) || end_frame_delay < major_delay_decrease_thresh) {
> +            spice_debug("downgrade due to short video delay (last=%u, past-max=%u",
> +                end_frame_delay, client_state->max_video_latency);
> +            mjpeg_encoder_handle_negative_client_stream_report(encoder,
> +                                                               end_frame_mm_time);
> +        } else if (!num_drops) {
> +            mjpeg_encoder_handle_positive_client_stream_report(encoder,
> +                                                               start_frame_mm_time);
> +
> +        }
> +    }
> +}
> +
> +void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder)
> +{
> +    encoder->rate_control.server_state.num_frames_dropped++;
> +    mjpeg_encoder_process_server_drops(encoder);
> +}
> +
> +/*
> + * decrease the bit rate if the drop rate on the sever side exceeds a pre defined
> + * threshold.
> + */
> +static void mjpeg_encoder_process_server_drops(MJpegEncoder *encoder)
> +{
> +    MJpegEncoderServerState *server_state = &encoder->rate_control.server_state;
> +    uint32_t num_frames_total;
> +    double drop_factor;
> +    uint32_t fps;
> +
> +    fps = MIN(encoder->rate_control.fps, mjpeg_encoder_get_source_fps(encoder));
> +    if (server_state->num_frames_encoded < fps * MJPEG_SERVER_STATUS_EVAL_FPS_INTERVAL) {
> +        return;
> +    }
> +
> +    num_frames_total = server_state->num_frames_dropped + server_state->num_frames_encoded;
> +    drop_factor = ((double)server_state->num_frames_dropped) / num_frames_total;
> +
> +    spice_debug("#drops %u total %u fps %u src-fps %u",
> +                server_state->num_frames_dropped,
> +                num_frames_total,
> +                encoder->rate_control.fps,
> +                mjpeg_encoder_get_source_fps(encoder));
> +
> +    if (drop_factor > MJPEG_SERVER_STATUS_DOWNGRADE_DROP_FACTOR_TH) {
> +        mjpeg_encoder_decrease_bit_rate(encoder);
> +    }
> +    server_state->num_frames_encoded = 0;
> +    server_state->num_frames_dropped = 0;
> +}
> +
> +uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder)
> +{
> +    return encoder->rate_control.byte_rate * 8;
> +}
> +
> +void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats *stats)
> +{
> +    spice_assert(encoder != NULL && stats != NULL);
> +    stats->starting_bit_rate = encoder->starting_bit_rate;
> +    stats->cur_bit_rate = mjpeg_encoder_get_bit_rate(encoder);
> +    stats->avg_quality = (double)encoder->avg_quality / encoder->num_frames;
> +}
> +
> +MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
> +                                MJpegEncoderRateControlCbs *cbs,
> +                                void *cbs_opaque)
> +{
> +    MJpegEncoder *encoder = spice_new0(MJpegEncoder, 1);
> +
> +    encoder->first_frame = TRUE;
> +    encoder->rate_control.byte_rate = starting_bit_rate / 8;
> +    encoder->starting_bit_rate = starting_bit_rate;
> +
> +    if (cbs) {
> +        struct timespec time;
> +
> +        clock_gettime(CLOCK_MONOTONIC, &time);
> +        encoder->cbs = *cbs;
> +        encoder->cbs_opaque = cbs_opaque;
> +        mjpeg_encoder_reset_quality(encoder, MJPEG_QUALITY_SAMPLE_NUM / 2, 5, 0);
> +        encoder->rate_control.during_quality_eval = TRUE;
> +        encoder->rate_control.quality_eval_data.type = MJPEG_QUALITY_EVAL_TYPE_SET;
> +        encoder->rate_control.quality_eval_data.reason = MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE;
> +        encoder->rate_control.warmup_start_time = ((uint64_t) time.tv_sec) * 1000000000 + time.tv_nsec;
> +    } else {
> +        encoder->cbs.get_roundtrip_ms = NULL;
> +        mjpeg_encoder_reset_quality(encoder, MJPEG_LEGACY_STATIC_QUALITY_ID, MJPEG_MAX_FPS, 0);
> +    }
> +
> +    encoder->cinfo.err = jpeg_std_error(&encoder->jerr);
> +    jpeg_create_compress(&encoder->cinfo);
> +
> +    return encoder;
> +}
> diff --git a/server/mjpeg-encoder.h b/server/mjpeg-encoder.h
> new file mode 100644
> index 0000000..d070e70
> --- /dev/null
> +++ b/server/mjpeg-encoder.h
> @@ -0,0 +1,100 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +
> +#ifndef _H_MJPEG_ENCODER
> +#define _H_MJPEG_ENCODER
> +
> +#include "red_common.h"
> +
> +enum {
> +    MJPEG_ENCODER_FRAME_UNSUPPORTED = -1,
> +    MJPEG_ENCODER_FRAME_DROP,
> +    MJPEG_ENCODER_FRAME_ENCODE_DONE,
> +};
> +
> +typedef struct MJpegEncoder MJpegEncoder;
> +
> +/*
> + * Callbacks required for controling and adjusting
> + * the stream bit rate:
> + * get_roundtrip_ms: roundtrip time in milliseconds
> + * get_source_fps: the input frame rate (#frames per second), i.e.,
> + * the rate of frames arriving from the guest to spice-server,
> + * before any drops.
> + */
> +typedef struct MJpegEncoderRateControlCbs {
> +    uint32_t (*get_roundtrip_ms)(void *opaque);
> +    uint32_t (*get_source_fps)(void *opaque);
> +    void (*update_client_playback_delay)(void *opaque, uint32_t delay_ms);
> +} MJpegEncoderRateControlCbs;
> +
> +typedef struct MJpegEncoderStats {
> +    uint64_t starting_bit_rate;
> +    uint64_t cur_bit_rate;
> +    double avg_quality;
> +} MJpegEncoderStats;
> +
> +MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
> +                                MJpegEncoderRateControlCbs *cbs, void *opaque);
> +void mjpeg_encoder_destroy(MJpegEncoder *encoder);
> +
> +int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
> +                               const SpiceBitmap *bitmap, int width, int height,
> +                               const SpiceRect *src,
> +                               int top_down, uint32_t frame_mm_time,
> +                               uint8_t **outbuf, size_t *outbuf_size,
> +                               int *data_size);
> +
> +/*
> + * bit rate control
> + */
> +
> +/*
> + * Data that should be periodically obtained from the client. The report contains:
> + * num_frames         : the number of frames that reached the client during the time
> + *                      the report is referring to.
> + * num_drops          : the part of the above frames that was dropped by the client due to
> + *                      late arrival time.
> + * start_frame_mm_time: the mm_time of the first frame included in the report
> + * end_frame_mm_time  : the mm_time of the last_frame included in the report
> + * end_frame_delay    : (end_frame_mm_time - client_mm_time)
> + * audio delay        : the latency of the audio playback.
> + *                      If there is no audio playback, set it to MAX_UINT.
> + *
> + */
> +void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,
> +                                        uint32_t num_frames,
> +                                        uint32_t num_drops,
> +                                        uint32_t start_frame_mm_time,
> +                                        uint32_t end_frame_mm_time,
> +                                        int32_t end_frame_delay,
> +                                        uint32_t audio_delay);
> +
> +/*
> + * Notify the encoder each time a frame is dropped due to pipe
> + * congestion.
> + * We can deduce the client state by the frame dropping rate in the server.
> + * Monitoring the frame drops can help in fine tuning the playback parameters
> + * when the client reports are delayed.
> + */
> +void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder);
> +
> +uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder);
> +void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats *stats);
> +
> +#endif
> diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c
> deleted file mode 100644
> index 9b331c1..0000000
> --- a/server/mjpeg_encoder.c
> +++ /dev/null
> @@ -1,1375 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifdef HAVE_CONFIG_H
> -#include <config.h>
> -#endif
> -
> -#include "red_common.h"
> -#include "mjpeg_encoder.h"
> -#include "utils.h"
> -#include <jerror.h>
> -#include <jpeglib.h>
> -#include <inttypes.h>
> -
> -#define MJPEG_MAX_FPS 25
> -#define MJPEG_MIN_FPS 1
> -
> -#define MJPEG_QUALITY_SAMPLE_NUM 7
> -static const int mjpeg_quality_samples[MJPEG_QUALITY_SAMPLE_NUM] = {20, 30, 40, 50, 60, 70, 80};
> -
> -#define MJPEG_LEGACY_STATIC_QUALITY_ID 5 // jpeg quality 70
> -
> -#define MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH 10
> -#define MJPEG_IMPROVE_QUALITY_FPS_PERMISSIVE_TH 5
> -
> -#define MJPEG_AVERAGE_SIZE_WINDOW 3
> -
> -#define MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES 3
> -#define MJPEG_LOW_FPS_RATE_TH 3
> -
> -#define MJPEG_SERVER_STATUS_EVAL_FPS_INTERVAL 1
> -#define MJPEG_SERVER_STATUS_DOWNGRADE_DROP_FACTOR_TH 0.1
> -
> -/*
> - * acting on positive client reports only if enough frame mm time
> - * has passed since the last bit rate change and the report.
> - * time
> - */
> -#define MJPEG_CLIENT_POSITIVE_REPORT_TIMEOUT 2000
> -#define MJPEG_CLIENT_POSITIVE_REPORT_STRICT_TIMEOUT 3000
> -
> -#define MJPEG_ADJUST_FPS_TIMEOUT 500
> -
> -/*
> - * avoid interrupting the playback when there are temporary
> - * incidents of instability (with respect to server and client drops)
> - */
> -#define MJPEG_MAX_CLIENT_PLAYBACK_DELAY 5000 // 5 sec
> -
> -/*
> - * The stream starts after lossless frames were sent to the client,
> - * and without rate control (except for pipe congestion). Thus, on the beginning
> - * of the stream, we might observe frame drops on the client and server side which
> - * are not necessarily related to mis-estimation of the bit rate, and we would
> - * like to wait till the stream stabilizes.
> - */
> -#define MJPEG_WARMUP_TIME 3000LL // 3 sec
> -
> -enum {
> -    MJPEG_QUALITY_EVAL_TYPE_SET,
> -    MJPEG_QUALITY_EVAL_TYPE_UPGRADE,
> -    MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE,
> -};
> -
> -enum {
> -    MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE,
> -    MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE,
> -};
> -
> -typedef struct MJpegEncoderQualityEval {
> -    int type;
> -    int reason;
> -
> -    uint64_t encoded_size_by_quality[MJPEG_QUALITY_SAMPLE_NUM];
> -    /* lower limit for the current evaluation round */
> -    int min_quality_id;
> -    int min_quality_fps; // min fps for the given quality
> -    /* upper limit for the current evaluation round */
> -    int max_quality_id;
> -    int max_quality_fps; // max fps for the given quality
> -    /* tracking the best sampled fps so far */
> -    int max_sampled_fps;
> -    int max_sampled_fps_quality_id;
> -} MJpegEncoderQualityEval;
> -
> -typedef struct MJpegEncoderClientState {
> -    int max_video_latency;
> -    uint32_t max_audio_latency;
> -} MJpegEncoderClientState;
> -
> -typedef struct MJpegEncoderServerState {
> -    uint32_t num_frames_encoded;
> -    uint32_t num_frames_dropped;
> -} MJpegEncoderServerState;
> -
> -typedef struct MJpegEncoderBitRateInfo {
> -    uint64_t change_start_time;
> -    uint64_t last_frame_time;
> -    uint32_t change_start_mm_time;
> -    int was_upgraded;
> -
> -    /* gathering data about the frames that
> -     * were encoded since the last bit rate change*/
> -    uint32_t num_enc_frames;
> -    uint64_t sum_enc_size;
> -} MJpegEncoderBitRateInfo;
> -
> -/*
> - * Adjusting the stream jpeg quality and frame rate (fps):
> - * When during_quality_eval=TRUE, we compress different frames with different
> - * jpeg quality. By considering (1) the resulting compression ratio, and (2) the available
> - * bit rate, we evaluate the max frame frequency for the stream with the given quality,
> - * and we choose the highest quality that will allow a reasonable frame rate.
> - * during_quality_eval is set for new streams and can also be set any time we want
> - * to re-evaluate the stream parameters (e.g., when the bit rate and/or
> - * compressed frame size significantly change).
> - */
> -typedef struct MJpegEncoderRateControl {
> -    int during_quality_eval;
> -    MJpegEncoderQualityEval quality_eval_data;
> -    MJpegEncoderBitRateInfo bit_rate_info;
> -    MJpegEncoderClientState client_state;
> -    MJpegEncoderServerState server_state;
> -
> -    uint64_t byte_rate;
> -    int quality_id;
> -    uint32_t fps;
> -    double adjusted_fps;
> -    uint64_t adjusted_fps_start_time;
> -    uint64_t adjusted_fps_num_frames;
> -
> -    /* the encoded frame size which the quality and the fps evaluation was based upon */
> -    uint64_t base_enc_size;
> -
> -    uint64_t last_enc_size;
> -
> -    uint64_t sum_recent_enc_size;
> -    uint32_t num_recent_enc_frames;
> -
> -    uint64_t warmup_start_time;
> -} MJpegEncoderRateControl;
> -
> -struct MJpegEncoder {
> -    uint8_t *row;
> -    uint32_t row_size;
> -    int first_frame;
> -
> -    struct jpeg_compress_struct cinfo;
> -    struct jpeg_error_mgr jerr;
> -
> -    unsigned int bytes_per_pixel; /* bytes per pixel of the input buffer */
> -    void (*pixel_converter)(void *src, uint8_t *dest);
> -
> -    MJpegEncoderRateControl rate_control;
> -    MJpegEncoderRateControlCbs cbs;
> -    void *cbs_opaque;
> -
> -    /* stats */
> -    uint64_t starting_bit_rate;
> -    uint64_t avg_quality;
> -    uint32_t num_frames;
> -};
> -
> -static void mjpeg_encoder_process_server_drops(MJpegEncoder *encoder);
> -static uint32_t get_min_required_playback_delay(uint64_t frame_enc_size,
> -                                                uint64_t byte_rate,
> -                                                uint32_t latency);
> -
> -static inline int rate_control_is_active(MJpegEncoder* encoder)
> -{
> -    return encoder->cbs.get_roundtrip_ms != NULL;
> -}
> -
> -void mjpeg_encoder_destroy(MJpegEncoder *encoder)
> -{
> -    free(encoder->cinfo.dest);
> -    jpeg_destroy_compress(&encoder->cinfo);
> -    free(encoder->row);
> -    free(encoder);
> -}
> -
> -static uint8_t mjpeg_encoder_get_bytes_per_pixel(MJpegEncoder *encoder)
> -{
> -    return encoder->bytes_per_pixel;
> -}
> -
> -#ifndef JCS_EXTENSIONS
> -/* Pixel conversion routines */
> -static void pixel_rgb24bpp_to_24(void *src_ptr, uint8_t *dest)
> -{
> -    uint8_t *src = src_ptr;
> -    /* libjpegs stores rgb, spice/win32 stores bgr */
> -    *dest++ = src[2]; /* red */
> -    *dest++ = src[1]; /* green */
> -    *dest++ = src[0]; /* blue */
> -}
> -
> -static void pixel_rgb32bpp_to_24(void *src, uint8_t *dest)
> -{
> -    uint32_t pixel = *(uint32_t *)src;
> -    *dest++ = (pixel >> 16) & 0xff;
> -    *dest++ = (pixel >>  8) & 0xff;
> -    *dest++ = (pixel >>  0) & 0xff;
> -}
> -#endif
> -
> -static void pixel_rgb16bpp_to_24(void *src, uint8_t *dest)
> -{
> -    uint16_t pixel = *(uint16_t *)src;
> -    *dest++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
> -    *dest++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
> -    *dest++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
> -}
> -
> -
> -/* code from libjpeg 8 to handle compression to a memory buffer
> - *
> - * Copyright (C) 1994-1996, Thomas G. Lane.
> - * Modified 2009 by Guido Vollbeding.
> - * This file is part of the Independent JPEG Group's software.
> - */
> -typedef struct {
> -  struct jpeg_destination_mgr pub; /* public fields */
> -
> -  unsigned char ** outbuffer;      /* target buffer */
> -  size_t * outsize;
> -  uint8_t * buffer;                /* start of buffer */
> -  size_t bufsize;
> -} mem_destination_mgr;
> -
> -static void init_mem_destination(j_compress_ptr cinfo)
> -{
> -}
> -
> -static boolean empty_mem_output_buffer(j_compress_ptr cinfo)
> -{
> -  size_t nextsize;
> -  uint8_t * nextbuffer;
> -  mem_destination_mgr *dest = (mem_destination_mgr *) cinfo->dest;
> -
> -  /* Try to allocate new buffer with double size */
> -  nextsize = dest->bufsize * 2;
> -  nextbuffer = malloc(nextsize);
> -
> -  if (nextbuffer == NULL)
> -    ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
> -
> -  memcpy(nextbuffer, dest->buffer, dest->bufsize);
> -
> -  free(dest->buffer);
> -
> -  dest->pub.next_output_byte = nextbuffer + dest->bufsize;
> -  dest->pub.free_in_buffer = dest->bufsize;
> -
> -  dest->buffer = nextbuffer;
> -  dest->bufsize = nextsize;
> -
> -  return TRUE;
> -}
> -
> -static void term_mem_destination(j_compress_ptr cinfo)
> -{
> -  mem_destination_mgr *dest = (mem_destination_mgr *) cinfo->dest;
> -
> -  *dest->outbuffer = dest->buffer;
> -  *dest->outsize = dest->bufsize;
> -}
> -
> -/*
> - * Prepare for output to a memory buffer.
> - * The caller may supply an own initial buffer with appropriate size.
> - * Otherwise, or when the actual data output exceeds the given size,
> - * the library adapts the buffer size as necessary.
> - * The standard library functions malloc/free are used for allocating
> - * larger memory, so the buffer is available to the application after
> - * finishing compression, and then the application is responsible for
> - * freeing the requested memory.
> - */
> -
> -static void
> -spice_jpeg_mem_dest(j_compress_ptr cinfo,
> -                    unsigned char ** outbuffer, size_t * outsize)
> -{
> -  mem_destination_mgr *dest;
> -#define OUTPUT_BUF_SIZE  4096 /* choose an efficiently fwrite'able size */
> -
> -  if (outbuffer == NULL || outsize == NULL) /* sanity check */
> -    ERREXIT(cinfo, JERR_BUFFER_SIZE);
> -
> -  /* The destination object is made permanent so that multiple JPEG images
> -   * can be written to the same buffer without re-executing jpeg_mem_dest.
> -   */
> -  if (cinfo->dest == NULL) { /* first time for this JPEG object? */
> -    cinfo->dest = spice_malloc(sizeof(mem_destination_mgr));
> -  }
> -
> -  dest = (mem_destination_mgr *) cinfo->dest;
> -  dest->pub.init_destination = init_mem_destination;
> -  dest->pub.empty_output_buffer = empty_mem_output_buffer;
> -  dest->pub.term_destination = term_mem_destination;
> -  dest->outbuffer = outbuffer;
> -  dest->outsize = outsize;
> -  if (*outbuffer == NULL || *outsize == 0) {
> -    /* Allocate initial buffer */
> -    *outbuffer = malloc(OUTPUT_BUF_SIZE);
> -    if (*outbuffer == NULL)
> -      ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
> -    *outsize = OUTPUT_BUF_SIZE;
> -  }
> -
> -  dest->pub.next_output_byte = dest->buffer = *outbuffer;
> -  dest->pub.free_in_buffer = dest->bufsize = *outsize;
> -}
> -/* end of code from libjpeg */
> -
> -static inline uint32_t mjpeg_encoder_get_source_fps(MJpegEncoder *encoder)
> -{
> -    return encoder->cbs.get_source_fps ?
> -        encoder->cbs.get_source_fps(encoder->cbs_opaque) : MJPEG_MAX_FPS;
> -}
> -
> -static inline uint32_t mjpeg_encoder_get_latency(MJpegEncoder *encoder)
> -{
> -    return encoder->cbs.get_roundtrip_ms ?
> -        encoder->cbs.get_roundtrip_ms(encoder->cbs_opaque) / 2 : 0;
> -}
> -
> -static uint32_t get_max_fps(uint64_t frame_size, uint64_t bytes_per_sec)
> -{
> -    double fps;
> -    double send_time_ms;
> -
> -    if (!bytes_per_sec) {
> -        return 0;
> -    }
> -    send_time_ms = frame_size * 1000.0 / bytes_per_sec;
> -    fps = send_time_ms ? 1000 / send_time_ms : MJPEG_MAX_FPS;
> -    return fps;
> -}
> -
> -static inline void mjpeg_encoder_reset_quality(MJpegEncoder *encoder,
> -                                               int quality_id,
> -                                               uint32_t fps,
> -                                               uint64_t frame_enc_size)
> -{
> -    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> -    double fps_ratio;
> -
> -    rate_control->during_quality_eval = FALSE;
> -
> -    if (rate_control->quality_id != quality_id) {
> -        rate_control->last_enc_size = 0;
> -    }
> -
> -    if (rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) {
> -        memset(&rate_control->server_state, 0, sizeof(MJpegEncoderServerState));
> -    }
> -    rate_control->quality_id = quality_id;
> -    memset(&rate_control->quality_eval_data, 0, sizeof(MJpegEncoderQualityEval));
> -    rate_control->quality_eval_data.max_quality_id = MJPEG_QUALITY_SAMPLE_NUM - 1;
> -    rate_control->quality_eval_data.max_quality_fps = MJPEG_MAX_FPS;
> -
> -    if (rate_control->adjusted_fps) {
> -        fps_ratio = rate_control->adjusted_fps / rate_control->fps;
> -    } else {
> -        fps_ratio = 1.5;
> -    }
> -    rate_control->fps = MAX(MJPEG_MIN_FPS, fps);
> -    rate_control->fps = MIN(MJPEG_MAX_FPS, rate_control->fps);
> -    rate_control->adjusted_fps = rate_control->fps*fps_ratio;
> -    spice_debug("adjusted-fps-ratio=%.2f adjusted-fps=%.2f", fps_ratio, rate_control->adjusted_fps);
> -    rate_control->adjusted_fps_start_time = 0;
> -    rate_control->adjusted_fps_num_frames = 0;
> -    rate_control->base_enc_size = frame_enc_size;
> -
> -    rate_control->sum_recent_enc_size = 0;
> -    rate_control->num_recent_enc_frames = 0;
> -}
> -
> -#define QUALITY_WAS_EVALUATED(encoder, quality) \
> -    ((encoder)->rate_control.quality_eval_data.encoded_size_by_quality[(quality)] != 0)
> -
> -/*
> - * Adjust the stream's jpeg quality and frame rate.
> - * We evaluate the compression ratio of different jpeg qualities;
> - * We compress successive frames with different qualities,
> - * and then we estimate the stream frame rate according to the currently
> - * evaluated jpeg quality and available bit rate.
> - *
> - * During quality evaluation, mjpeg_encoder_eval_quality is called before a new
> - * frame is encoded. mjpeg_encoder_eval_quality examines the encoding size of
> - * the previously encoded frame, and determines whether to continue evaluation
> - * (and chnages the quality for the frame that is going to be encoded),
> - * or stop evaluation (and sets the quality and frame rate for the stream).
> - * When qualities are scanned, we assume monotonicity of compression ratio
> - * as a function of jpeg quality. When we reach a quality with too small, or
> - * big enough compression ratio, we stop the evaluation and set the stream parameters.
> -*/
> -static inline void mjpeg_encoder_eval_quality(MJpegEncoder *encoder)
> -{
> -    MJpegEncoderRateControl *rate_control;
> -    MJpegEncoderQualityEval *quality_eval;
> -    uint32_t fps, src_fps;
> -    uint64_t enc_size;
> -    uint32_t final_quality_id;
> -    uint32_t final_fps;
> -    uint64_t final_quality_enc_size;
> -
> -    rate_control = &encoder->rate_control;
> -    quality_eval = &rate_control->quality_eval_data;
> -
> -    spice_assert(rate_control->during_quality_eval);
> -
> -    /* retrieving the encoded size of the last encoded frame */
> -    enc_size = quality_eval->encoded_size_by_quality[rate_control->quality_id];
> -    if (enc_size == 0) {
> -        spice_debug("size info missing");
> -        return;
> -    }
> -
> -    src_fps = mjpeg_encoder_get_source_fps(encoder);
> -
> -    fps = get_max_fps(enc_size, rate_control->byte_rate);
> -    spice_debug("mjpeg %p: jpeg %d: %.2f (KB) fps %d src-fps %u",
> -                encoder,
> -                mjpeg_quality_samples[rate_control->quality_id],
> -                enc_size / 1024.0,
> -                fps,
> -                src_fps);
> -
> -    if (fps > quality_eval->max_sampled_fps ||
> -        ((fps == quality_eval->max_sampled_fps || fps >= src_fps) &&
> -         rate_control->quality_id > quality_eval->max_sampled_fps_quality_id)) {
> -        quality_eval->max_sampled_fps = fps;
> -        quality_eval->max_sampled_fps_quality_id = rate_control->quality_id;
> -    }
> -
> -    /*
> -     * Choosing whether to evaluate another quality, or to complete evaluation
> -     * and set the stream parameters according to one of the qualities that
> -     * were already sampled.
> -     */
> -
> -    if (rate_control->quality_id > MJPEG_QUALITY_SAMPLE_NUM / 2 &&
> -        fps < MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH &&
> -        fps < src_fps) {
> -        /*
> -         * When the jpeg quality is bigger than the median quality, prefer a reasonable
> -         * frame rate over improving the quality
> -         */
> -        spice_debug("fps < %d && (fps < src_fps), quality %d",
> -                MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH,
> -                mjpeg_quality_samples[rate_control->quality_id]);
> -        if (QUALITY_WAS_EVALUATED(encoder, rate_control->quality_id - 1)) {
> -            /* the next worse quality was already evaluated and it passed the frame
> -             * rate thresholds (we know that, because we continued evaluating a better
> -             * quality) */
> -            rate_control->quality_id--;
> -            goto complete_sample;
> -        } else {
> -            /* evaluate the next worse quality */
> -            rate_control->quality_id--;
> -        }
> -    } else if ((fps > MJPEG_IMPROVE_QUALITY_FPS_PERMISSIVE_TH &&
> -                fps >= 0.66 * quality_eval->min_quality_fps) || fps >= src_fps) {
> -        /* When the jpeg quality is worse than the median one (see first condition), we allow a less
> -           strict threshold for fps, in order to improve the jpeg quality */
> -        if (rate_control->quality_id + 1 == MJPEG_QUALITY_SAMPLE_NUM ||
> -            rate_control->quality_id >= quality_eval->max_quality_id ||
> -            QUALITY_WAS_EVALUATED(encoder, rate_control->quality_id + 1)) {
> -            /* best quality has been reached, or the next (better) quality was
> -             * already evaluated and didn't pass the fps thresholds */
> -            goto complete_sample;
> -        } else {
> -            if (rate_control->quality_id == MJPEG_QUALITY_SAMPLE_NUM / 2 &&
> -                fps < MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH &&
> -                fps < src_fps) {
> -                goto complete_sample;
> -            }
> -            /* evaluate the next quality as well*/
> -            rate_control->quality_id++;
> -        }
> -    } else { // very small frame rate, try to improve by downgrading the quality
> -        if (rate_control->quality_id == 0 ||
> -            rate_control->quality_id <= quality_eval->min_quality_id) {
> -            goto complete_sample;
> -        } else if (QUALITY_WAS_EVALUATED(encoder, rate_control->quality_id - 1)) {
> -            rate_control->quality_id--;
> -            goto complete_sample;
> -        } else {
> -            /* evaluate the next worse quality */
> -            rate_control->quality_id--;
> -        }
> -    }
> -    return;
> -
> -complete_sample:
> -    if (quality_eval->max_sampled_fps != 0) {
> -        /* covering a case were monotonicity was violated and we sampled
> -           a better jepg quality, with better frame rate. */
> -        final_quality_id = MAX(rate_control->quality_id,
> -                               quality_eval->max_sampled_fps_quality_id);
> -    } else {
> -        final_quality_id = rate_control->quality_id;
> -    }
> -    final_quality_enc_size = quality_eval->encoded_size_by_quality[final_quality_id];
> -    final_fps = get_max_fps(final_quality_enc_size,
> -                            rate_control->byte_rate);
> -
> -    if (final_quality_id == quality_eval->min_quality_id) {
> -        final_fps = MAX(final_fps, quality_eval->min_quality_fps);
> -    }
> -    if (final_quality_id == quality_eval->max_quality_id) {
> -        final_fps = MIN(final_fps, quality_eval->max_quality_fps);
> -    }
> -    mjpeg_encoder_reset_quality(encoder, final_quality_id, final_fps, final_quality_enc_size);
> -    rate_control->sum_recent_enc_size = final_quality_enc_size;
> -    rate_control->num_recent_enc_frames = 1;
> -
> -    spice_debug("MJpeg quality sample end %p: quality %d fps %d",
> -                encoder, mjpeg_quality_samples[rate_control->quality_id], rate_control->fps);
> -    if (encoder->cbs.update_client_playback_delay) {
> -        uint32_t latency = mjpeg_encoder_get_latency(encoder);
> -        uint32_t min_delay = get_min_required_playback_delay(final_quality_enc_size,
> -                                                             rate_control->byte_rate,
> -                                                             latency);
> -
> -        encoder->cbs.update_client_playback_delay(encoder->cbs_opaque, min_delay);
> -    }
> -}
> -
> -static void mjpeg_encoder_quality_eval_set_upgrade(MJpegEncoder *encoder,
> -                                                   int reason,
> -                                                   uint32_t min_quality_id,
> -                                                   uint32_t min_quality_fps)
> -{
> -    MJpegEncoderQualityEval *quality_eval = &encoder->rate_control.quality_eval_data;
> -
> -    encoder->rate_control.during_quality_eval = TRUE;
> -    quality_eval->type = MJPEG_QUALITY_EVAL_TYPE_UPGRADE;
> -    quality_eval->reason = reason;
> -    quality_eval->min_quality_id = min_quality_id;
> -    quality_eval->min_quality_fps = min_quality_fps;
> -}
> -
> -static void mjpeg_encoder_quality_eval_set_downgrade(MJpegEncoder *encoder,
> -                                                     int reason,
> -                                                     uint32_t max_quality_id,
> -                                                     uint32_t max_quality_fps)
> -{
> -    MJpegEncoderQualityEval *quality_eval = &encoder->rate_control.quality_eval_data;
> -
> -    encoder->rate_control.during_quality_eval = TRUE;
> -    quality_eval->type = MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE;
> -    quality_eval->reason = reason;
> -    quality_eval->max_quality_id = max_quality_id;
> -    quality_eval->max_quality_fps = max_quality_fps;
> -}
> -
> -static void mjpeg_encoder_adjust_params_to_bit_rate(MJpegEncoder *encoder)
> -{
> -    MJpegEncoderRateControl *rate_control;
> -    MJpegEncoderQualityEval *quality_eval;
> -    uint64_t new_avg_enc_size = 0;
> -    uint32_t new_fps;
> -    uint32_t latency = 0;
> -    uint32_t src_fps;
> -
> -    spice_assert(rate_control_is_active(encoder));
> -
> -    rate_control = &encoder->rate_control;
> -    quality_eval = &rate_control->quality_eval_data;
> -
> -    if (!rate_control->last_enc_size) {
> -        spice_debug("missing sample size");
> -        return;
> -    }
> -
> -    if (rate_control->during_quality_eval) {
> -        quality_eval->encoded_size_by_quality[rate_control->quality_id] = rate_control->last_enc_size;
> -        mjpeg_encoder_eval_quality(encoder);
> -        return;
> -    }
> -
> -    if (!rate_control->num_recent_enc_frames) {
> -        spice_debug("No recent encoded frames");
> -        return;
> -    }
> -
> -    if (rate_control->num_recent_enc_frames < MJPEG_AVERAGE_SIZE_WINDOW &&
> -        rate_control->num_recent_enc_frames < rate_control->fps) {
> -        goto end;
> -    }
> -
> -    latency = mjpeg_encoder_get_latency(encoder);
> -    new_avg_enc_size = rate_control->sum_recent_enc_size /
> -                       rate_control->num_recent_enc_frames;
> -    new_fps = get_max_fps(new_avg_enc_size, rate_control->byte_rate);
> -
> -    spice_debug("cur-fps=%u new-fps=%u (new/old=%.2f) |"
> -                "bit-rate=%.2f (Mbps) latency=%u (ms) quality=%d |"
> -                " new-size-avg %"PRIu64" , base-size %"PRIu64", (new/old=%.2f) ",
> -                rate_control->fps, new_fps, ((double)new_fps)/rate_control->fps,
> -                ((double)rate_control->byte_rate*8)/1024/1024,
> -                latency,
> -                mjpeg_quality_samples[rate_control->quality_id],
> -                new_avg_enc_size, rate_control->base_enc_size,
> -                rate_control->base_enc_size ?
> -                    ((double)new_avg_enc_size) / rate_control->base_enc_size :
> -                    1);
> -
> -     src_fps = mjpeg_encoder_get_source_fps(encoder);
> -
> -    /*
> -     * The ratio between the new_fps and the current fps reflects the changes
> -     * in latency and frame size. When the change passes a threshold,
> -     * we re-evaluate the quality and frame rate.
> -     */
> -    if (new_fps > rate_control->fps &&
> -        (rate_control->fps < src_fps || rate_control->quality_id < MJPEG_QUALITY_SAMPLE_NUM - 1)) {
> -        spice_debug("mjpeg %p FPS CHANGE >> :  re-evaluating params", encoder);
> -        mjpeg_encoder_quality_eval_set_upgrade(encoder, MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE,
> -                                               rate_control->quality_id, /* fps has improved -->
> -                                                                            don't allow stream quality
> -                                                                            to deteriorate */
> -                                               rate_control->fps);
> -    } else if (new_fps < rate_control->fps && new_fps < src_fps) {
> -        spice_debug("mjpeg %p FPS CHANGE << : re-evaluating params", encoder);
> -        mjpeg_encoder_quality_eval_set_downgrade(encoder, MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE,
> -                                                 rate_control->quality_id,
> -                                                 rate_control->fps);
> -    }
> -end:
> -    if (rate_control->during_quality_eval) {
> -        quality_eval->encoded_size_by_quality[rate_control->quality_id] = new_avg_enc_size;
> -        mjpeg_encoder_eval_quality(encoder);
> -    } else {
> -        mjpeg_encoder_process_server_drops(encoder);
> -    }
> -}
> -
> -/*
> - * The actual frames distribution does not necessarily fit the condition "at least
> - * one frame every (1000/rate_contorl->fps) milliseconds".
> - * For keeping the average fps close to the defined fps, we periodically
> - * measure the current average fps, and modify rate_control->adjusted_fps accordingly.
> - * Then, we use (1000/rate_control->adjusted_fps) as the interval between frames.
> - */
> -static void mjpeg_encoder_adjust_fps(MJpegEncoder *encoder, uint64_t now)
> -{
> -    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> -    uint64_t adjusted_fps_time_passed;
> -
> -    spice_assert(rate_control_is_active(encoder));
> -
> -    adjusted_fps_time_passed = (now - rate_control->adjusted_fps_start_time) / 1000 / 1000;
> -
> -    if (!rate_control->during_quality_eval &&
> -        adjusted_fps_time_passed > MJPEG_ADJUST_FPS_TIMEOUT &&
> -        adjusted_fps_time_passed > 1000 / rate_control->adjusted_fps) {
> -        double avg_fps;
> -        double fps_ratio;
> -
> -        avg_fps = ((double)rate_control->adjusted_fps_num_frames*1000) /
> -                  adjusted_fps_time_passed;
> -        spice_debug("#frames-adjust=%"PRIu64" #adjust-time=%"PRIu64" avg-fps=%.2f",
> -                    rate_control->adjusted_fps_num_frames, adjusted_fps_time_passed, avg_fps);
> -        spice_debug("defined=%u old-adjusted=%.2f", rate_control->fps, rate_control->adjusted_fps);
> -        fps_ratio = avg_fps / rate_control->fps;
> -        if (avg_fps + 0.5 < rate_control->fps &&
> -            mjpeg_encoder_get_source_fps(encoder) > avg_fps) {
> -            double new_adjusted_fps = avg_fps ?
> -                                               (rate_control->adjusted_fps/fps_ratio) :
> -                                               rate_control->adjusted_fps * 2;
> -
> -            rate_control->adjusted_fps = MIN(rate_control->fps*2, new_adjusted_fps);
> -            spice_debug("new-adjusted-fps=%.2f", rate_control->adjusted_fps);
> -        } else if (rate_control->fps + 0.5 < avg_fps) {
> -            double new_adjusted_fps = rate_control->adjusted_fps / fps_ratio;
> -
> -            rate_control->adjusted_fps = MAX(rate_control->fps, new_adjusted_fps);
> -            spice_debug("new-adjusted-fps=%.2f", rate_control->adjusted_fps);
> -        }
> -        rate_control->adjusted_fps_start_time = now;
> -        rate_control->adjusted_fps_num_frames = 0;
> -    }
> -}
> -
> -/*
> - * dest must be either NULL or allocated by malloc, since it might be freed
> - * during the encoding, if its size is too small.
> - *
> - * return:
> - *  MJPEG_ENCODER_FRAME_UNSUPPORTED : frame cannot be encoded
> - *  MJPEG_ENCODER_FRAME_DROP        : frame should be dropped. This value can only be returned
> - *                                    if mjpeg rate control is active.
> - *  MJPEG_ENCODER_FRAME_ENCODE_DONE : frame encoding started. Continue with
> - *                                    mjpeg_encoder_encode_scanline.
> - */
> -static int mjpeg_encoder_start_frame(MJpegEncoder *encoder,
> -                                     SpiceBitmapFmt format,
> -                                     int width, int height,
> -                                     uint8_t **dest, size_t *dest_len,
> -                                     uint32_t frame_mm_time)
> -{
> -    uint32_t quality;
> -
> -    if (rate_control_is_active(encoder)) {
> -        MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> -        uint64_t now;
> -        uint64_t interval;
> -
> -        now = red_get_monotonic_time();
> -
> -        if (!rate_control->adjusted_fps_start_time) {
> -            rate_control->adjusted_fps_start_time = now;
> -        }
> -        mjpeg_encoder_adjust_fps(encoder, now);
> -        interval = (now - rate_control->bit_rate_info.last_frame_time);
> -
> -        if (interval < (1000*1000*1000) / rate_control->adjusted_fps) {
> -            return MJPEG_ENCODER_FRAME_DROP;
> -        }
> -
> -        mjpeg_encoder_adjust_params_to_bit_rate(encoder);
> -
> -        if (!rate_control->during_quality_eval ||
> -            rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE) {
> -            MJpegEncoderBitRateInfo *bit_rate_info;
> -
> -            bit_rate_info = &encoder->rate_control.bit_rate_info;
> -
> -            if (!bit_rate_info->change_start_time) {
> -                bit_rate_info->change_start_time = now;
> -                bit_rate_info->change_start_mm_time = frame_mm_time;
> -            }
> -            bit_rate_info->last_frame_time = now;
> -        }
> -    }
> -
> -    encoder->cinfo.in_color_space   = JCS_RGB;
> -    encoder->cinfo.input_components = 3;
> -    encoder->pixel_converter = NULL;
> -
> -    switch (format) {
> -    case SPICE_BITMAP_FMT_32BIT:
> -    case SPICE_BITMAP_FMT_RGBA:
> -        encoder->bytes_per_pixel = 4;
> -#ifdef JCS_EXTENSIONS
> -        encoder->cinfo.in_color_space   = JCS_EXT_BGRX;
> -        encoder->cinfo.input_components = 4;
> -#else
> -        encoder->pixel_converter = pixel_rgb32bpp_to_24;
> -#endif
> -        break;
> -    case SPICE_BITMAP_FMT_16BIT:
> -        encoder->bytes_per_pixel = 2;
> -        encoder->pixel_converter = pixel_rgb16bpp_to_24;
> -        break;
> -    case SPICE_BITMAP_FMT_24BIT:
> -        encoder->bytes_per_pixel = 3;
> -#ifdef JCS_EXTENSIONS
> -        encoder->cinfo.in_color_space = JCS_EXT_BGR;
> -#else
> -        encoder->pixel_converter = pixel_rgb24bpp_to_24;
> -#endif
> -        break;
> -    default:
> -        spice_debug("unsupported format %d", format);
> -        return MJPEG_ENCODER_FRAME_UNSUPPORTED;
> -    }
> -
> -    if (encoder->pixel_converter != NULL) {
> -        unsigned int stride = width * 3;
> -        /* check for integer overflow */
> -        if (stride < width) {
> -            return MJPEG_ENCODER_FRAME_UNSUPPORTED;
> -        }
> -        if (encoder->row_size < stride) {
> -            encoder->row = spice_realloc(encoder->row, stride);
> -            encoder->row_size = stride;
> -        }
> -    }
> -
> -    spice_jpeg_mem_dest(&encoder->cinfo, dest, dest_len);
> -
> -    encoder->cinfo.image_width      = width;
> -    encoder->cinfo.image_height     = height;
> -    jpeg_set_defaults(&encoder->cinfo);
> -    encoder->cinfo.dct_method       = JDCT_IFAST;
> -    quality = mjpeg_quality_samples[encoder->rate_control.quality_id];
> -    jpeg_set_quality(&encoder->cinfo, quality, TRUE);
> -    jpeg_start_compress(&encoder->cinfo, encoder->first_frame);
> -
> -    encoder->num_frames++;
> -    encoder->avg_quality += quality;
> -    return MJPEG_ENCODER_FRAME_ENCODE_DONE;
> -}
> -
> -static int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder,
> -                                         uint8_t *src_pixels,
> -                                         size_t image_width)
> -{
> -    unsigned int scanlines_written;
> -    uint8_t *row;
> -
> -    row = encoder->row;
> -    if (encoder->pixel_converter) {
> -        unsigned int x;
> -        for (x = 0; x < image_width; x++) {
> -            /* src_pixels is expected to be 4 bytes aligned */
> -            encoder->pixel_converter(src_pixels, row);
> -            row += 3;
> -            src_pixels += encoder->bytes_per_pixel;
> -        }
> -        scanlines_written = jpeg_write_scanlines(&encoder->cinfo, &encoder->row, 1);
> -    } else {
> -        scanlines_written = jpeg_write_scanlines(&encoder->cinfo, &src_pixels, 1);
> -    }
> -    if (scanlines_written == 0) { /* Not enough space */
> -        jpeg_abort_compress(&encoder->cinfo);
> -        encoder->rate_control.last_enc_size = 0;
> -        return 0;
> -    }
> -
> -    return scanlines_written;
> -}
> -
> -static size_t mjpeg_encoder_end_frame(MJpegEncoder *encoder)
> -{
> -    mem_destination_mgr *dest = (mem_destination_mgr *) encoder->cinfo.dest;
> -    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> -
> -    jpeg_finish_compress(&encoder->cinfo);
> -
> -    encoder->first_frame = FALSE;
> -    rate_control->last_enc_size = dest->pub.next_output_byte - dest->buffer;
> -    rate_control->server_state.num_frames_encoded++;
> -
> -    if (!rate_control->during_quality_eval ||
> -        rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE) {
> -
> -        if (!rate_control->during_quality_eval) {
> -            if (rate_control->num_recent_enc_frames >= MJPEG_AVERAGE_SIZE_WINDOW) {
> -                rate_control->num_recent_enc_frames = 0;
> -                rate_control->sum_recent_enc_size = 0;
> -            }
> -            rate_control->sum_recent_enc_size += rate_control->last_enc_size;
> -            rate_control->num_recent_enc_frames++;
> -            rate_control->adjusted_fps_num_frames++;
> -        }
> -        rate_control->bit_rate_info.sum_enc_size += encoder->rate_control.last_enc_size;
> -        rate_control->bit_rate_info.num_enc_frames++;
> -    }
> -    return encoder->rate_control.last_enc_size;
> -}
> -
> -static inline uint8_t *get_image_line(SpiceChunks *chunks, size_t *offset,
> -                                      int *chunk_nr, int stride)
> -{
> -    uint8_t *ret;
> -    SpiceChunk *chunk;
> -
> -    chunk = &chunks->chunk[*chunk_nr];
> -
> -    if (*offset == chunk->len) {
> -        if (*chunk_nr == chunks->num_chunks - 1) {
> -            return NULL; /* Last chunk */
> -        }
> -        *offset = 0;
> -        (*chunk_nr)++;
> -        chunk = &chunks->chunk[*chunk_nr];
> -    }
> -
> -    if (chunk->len - *offset < stride) {
> -        spice_warning("bad chunk alignment");
> -        return NULL;
> -    }
> -    ret = chunk->data + *offset;
> -    *offset += stride;
> -    return ret;
> -}
> -
> -static int encode_frame(MJpegEncoder *encoder, const SpiceRect *src,
> -                        const SpiceBitmap *image, int top_down)
> -{
> -    SpiceChunks *chunks;
> -    uint32_t image_stride;
> -    size_t offset;
> -    int i, chunk;
> -
> -    chunks = image->data;
> -    offset = 0;
> -    chunk = 0;
> -    image_stride = image->stride;
> -
> -    const int skip_lines = top_down ? src->top : image->y - (src->bottom - 0);
> -    for (i = 0; i < skip_lines; i++) {
> -        get_image_line(chunks, &offset, &chunk, image_stride);
> -    }
> -
> -    const unsigned int stream_height = src->bottom - src->top;
> -    const unsigned int stream_width = src->right - src->left;
> -
> -    for (i = 0; i < stream_height; i++) {
> -        uint8_t *src_line = get_image_line(chunks, &offset, &chunk, image_stride);
> -
> -        if (!src_line) {
> -            return FALSE;
> -        }
> -
> -        src_line += src->left * mjpeg_encoder_get_bytes_per_pixel(encoder);
> -        if (mjpeg_encoder_encode_scanline(encoder, src_line, stream_width) == 0) {
> -            return FALSE;
> -        }
> -    }
> -
> -    return TRUE;
> -}
> -
> -int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
> -                               const SpiceBitmap *bitmap, int width, int height,
> -                               const SpiceRect *src,
> -                               int top_down, uint32_t frame_mm_time,
> -                               uint8_t **outbuf, size_t *outbuf_size,
> -                               int *data_size)
> -{
> -    int ret = mjpeg_encoder_start_frame(encoder, bitmap->format,
> -                                    width, height, outbuf, outbuf_size,
> -                                    frame_mm_time);
> -    if (ret != MJPEG_ENCODER_FRAME_ENCODE_DONE) {
> -        return ret;
> -    }
> -
> -    if (!encode_frame(encoder, src, bitmap, top_down)) {
> -        return MJPEG_ENCODER_FRAME_UNSUPPORTED;
> -    }
> -
> -    *data_size = mjpeg_encoder_end_frame(encoder);
> -
> -    return MJPEG_ENCODER_FRAME_ENCODE_DONE;
> -}
> -
> -
> -static void mjpeg_encoder_quality_eval_stop(MJpegEncoder *encoder)
> -{
> -    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> -    uint32_t quality_id;
> -    uint32_t fps;
> -
> -    if (!rate_control->during_quality_eval) {
> -        return;
> -    }
> -    switch (rate_control->quality_eval_data.type) {
> -    case MJPEG_QUALITY_EVAL_TYPE_UPGRADE:
> -        quality_id = rate_control->quality_eval_data.min_quality_id;
> -        fps = rate_control->quality_eval_data.min_quality_fps;
> -        break;
> -    case MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE:
> -        quality_id = rate_control->quality_eval_data.max_quality_id;
> -        fps = rate_control->quality_eval_data.max_quality_fps;
> -        break;
> -    case MJPEG_QUALITY_EVAL_TYPE_SET:
> -        quality_id = MJPEG_QUALITY_SAMPLE_NUM / 2;
> -        fps = MJPEG_MAX_FPS / 2;
> -        break;
> -    default:
> -        spice_warning("unexected");
> -        return;
> -    }
> -    mjpeg_encoder_reset_quality(encoder, quality_id, fps, 0);
> -    spice_debug("during quality evaluation: canceling."
> -                "reset quality to %d fps %d",
> -                mjpeg_quality_samples[rate_control->quality_id], rate_control->fps);
> -}
> -
> -static void mjpeg_encoder_decrease_bit_rate(MJpegEncoder *encoder)
> -{
> -    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> -    MJpegEncoderBitRateInfo *bit_rate_info = &rate_control->bit_rate_info;
> -    uint64_t measured_byte_rate;
> -    uint32_t measured_fps;
> -    uint64_t decrease_size;
> -
> -    mjpeg_encoder_quality_eval_stop(encoder);
> -
> -    rate_control->client_state.max_video_latency = 0;
> -    rate_control->client_state.max_audio_latency = 0;
> -    if (rate_control->warmup_start_time) {
> -        uint64_t now;
> -
> -        now = red_get_monotonic_time();
> -        if (now - rate_control->warmup_start_time < MJPEG_WARMUP_TIME*1000*1000) {
> -            spice_debug("during warmup. ignoring");
> -            return;
> -        } else {
> -            rate_control->warmup_start_time = 0;
> -        }
> -    }
> -
> -    if (bit_rate_info->num_enc_frames > MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES ||
> -        bit_rate_info->num_enc_frames > rate_control->fps) {
> -        double duration_sec;
> -
> -        duration_sec = (bit_rate_info->last_frame_time - bit_rate_info->change_start_time);
> -        duration_sec /= (1000.0 * 1000.0 * 1000.0);
> -        measured_byte_rate = bit_rate_info->sum_enc_size / duration_sec;
> -        measured_fps = bit_rate_info->num_enc_frames / duration_sec;
> -        decrease_size = bit_rate_info->sum_enc_size / bit_rate_info->num_enc_frames;
> -        spice_debug("bit rate esitimation %.2f (Mbps) fps %u",
> -                    measured_byte_rate*8/1024.0/1024,
> -                    measured_fps);
> -    } else {
> -        measured_byte_rate = rate_control->byte_rate;
> -        measured_fps = rate_control->fps;
> -        decrease_size = measured_byte_rate/measured_fps;
> -        spice_debug("bit rate not re-estimated %.2f (Mbps) fps %u",
> -                    measured_byte_rate*8/1024.0/1024,
> -                    measured_fps);
> -    }
> -
> -    measured_byte_rate = MIN(rate_control->byte_rate, measured_byte_rate);
> -
> -    if (decrease_size >=  measured_byte_rate) {
> -        decrease_size = measured_byte_rate / 2;
> -    }
> -
> -    rate_control->byte_rate = measured_byte_rate - decrease_size;
> -    bit_rate_info->change_start_time = 0;
> -    bit_rate_info->change_start_mm_time = 0;
> -    bit_rate_info->last_frame_time = 0;
> -    bit_rate_info->num_enc_frames = 0;
> -    bit_rate_info->sum_enc_size = 0;
> -    bit_rate_info->was_upgraded = FALSE;
> -
> -    spice_debug("decrease bit rate %.2f (Mbps)", rate_control->byte_rate * 8 / 1024.0/1024.0);
> -    mjpeg_encoder_quality_eval_set_downgrade(encoder,
> -                                             MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE,
> -                                             rate_control->quality_id,
> -                                             rate_control->fps);
> -}
> -
> -static void mjpeg_encoder_handle_negative_client_stream_report(MJpegEncoder *encoder,
> -                                                               uint32_t report_end_frame_mm_time)
> -{
> -    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> -
> -    spice_debug(NULL);
> -
> -    if ((rate_control->bit_rate_info.change_start_mm_time > report_end_frame_mm_time ||
> -        !rate_control->bit_rate_info.change_start_mm_time) &&
> -         !rate_control->bit_rate_info.was_upgraded) {
> -        spice_debug("ignoring, a downgrade has already occurred later to the report time");
> -        return;
> -    }
> -
> -    mjpeg_encoder_decrease_bit_rate(encoder);
> -}
> -
> -static void mjpeg_encoder_increase_bit_rate(MJpegEncoder *encoder)
> -{
> -    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> -    MJpegEncoderBitRateInfo *bit_rate_info = &rate_control->bit_rate_info;
> -    uint64_t measured_byte_rate;
> -    uint32_t measured_fps;
> -    uint64_t increase_size;
> -
> -
> -    if (bit_rate_info->num_enc_frames > MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES ||
> -        bit_rate_info->num_enc_frames > rate_control->fps) {
> -        uint64_t avg_frame_size;
> -        double duration_sec;
> -
> -        duration_sec = (bit_rate_info->last_frame_time - bit_rate_info->change_start_time);
> -        duration_sec /= (1000.0 * 1000.0 * 1000.0);
> -        measured_byte_rate = bit_rate_info->sum_enc_size / duration_sec;
> -        measured_fps = bit_rate_info->num_enc_frames / duration_sec;
> -        avg_frame_size = bit_rate_info->sum_enc_size / bit_rate_info->num_enc_frames;
> -        spice_debug("bit rate esitimation %.2f (Mbps) defined %.2f"
> -                    " fps %u avg-frame-size=%.2f (KB)",
> -                    measured_byte_rate*8/1024.0/1024,
> -                    rate_control->byte_rate*8/1024.0/1024,
> -                    measured_fps,
> -                    avg_frame_size/1024.0);
> -        increase_size = avg_frame_size;
> -    } else {
> -        spice_debug("not enough samples for measuring the bit rate. no change");
> -        return;
> -    }
> -
> -
> -    mjpeg_encoder_quality_eval_stop(encoder);
> -
> -    if (measured_byte_rate + increase_size < rate_control->byte_rate) {
> -        spice_debug("measured byte rate is small: not upgrading, just re-evaluating");
> -    } else {
> -        rate_control->byte_rate = MIN(measured_byte_rate, rate_control->byte_rate) + increase_size;
> -    }
> -
> -    bit_rate_info->change_start_time = 0;
> -    bit_rate_info->change_start_mm_time = 0;
> -    bit_rate_info->last_frame_time = 0;
> -    bit_rate_info->num_enc_frames = 0;
> -    bit_rate_info->sum_enc_size = 0;
> -    bit_rate_info->was_upgraded = TRUE;
> -
> -    spice_debug("increase bit rate %.2f (Mbps)", rate_control->byte_rate * 8 / 1024.0/1024.0);
> -    mjpeg_encoder_quality_eval_set_upgrade(encoder,
> -                                           MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE,
> -                                           rate_control->quality_id,
> -                                           rate_control->fps);
> -}
> -
> -static void mjpeg_encoder_handle_positive_client_stream_report(MJpegEncoder *encoder,
> -                                                               uint32_t report_start_frame_mm_time)
> -{
> -    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> -    MJpegEncoderBitRateInfo *bit_rate_info = &rate_control->bit_rate_info;
> -    int stable_client_mm_time;
> -    int timeout;
> -
> -    if (rate_control->during_quality_eval &&
> -        rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) {
> -        spice_debug("during quality evaluation (rate change). ignoring report");
> -        return;
> -    }
> -
> -    if ((rate_control->fps > MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH ||
> -         rate_control->fps >= mjpeg_encoder_get_source_fps(encoder)) &&
> -         rate_control->quality_id > MJPEG_QUALITY_SAMPLE_NUM / 2) {
> -        timeout = MJPEG_CLIENT_POSITIVE_REPORT_STRICT_TIMEOUT;
> -    } else {
> -        timeout = MJPEG_CLIENT_POSITIVE_REPORT_TIMEOUT;
> -    }
> -
> -    stable_client_mm_time = (int)report_start_frame_mm_time - bit_rate_info->change_start_mm_time;
> -
> -    if (!bit_rate_info->change_start_mm_time || stable_client_mm_time < timeout) {
> -        /* assessing the stability of the current setting and only then
> -         * respond to the report */
> -        spice_debug("no drops, but not enough time has passed for assessing"
> -                    "the playback stability since the last bit rate change");
> -        return;
> -    }
> -    mjpeg_encoder_increase_bit_rate(encoder);
> -}
> -
> -/*
> - * the video playback jitter buffer should be at least (send_time*2 + net_latency) for
> - * preventing underflow
> - */
> -static uint32_t get_min_required_playback_delay(uint64_t frame_enc_size,
> -                                                uint64_t byte_rate,
> -                                                uint32_t latency)
> -{
> -    uint32_t one_frame_time;
> -    uint32_t min_delay;
> -
> -    if (!frame_enc_size || !byte_rate) {
> -        return latency;
> -    }
> -    one_frame_time = (frame_enc_size*1000)/byte_rate;
> -
> -    min_delay = MIN(one_frame_time*2 + latency, MJPEG_MAX_CLIENT_PLAYBACK_DELAY);
> -    return min_delay;
> -}
> -
> -#define MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR 0.5
> -#define MJPEG_VIDEO_VS_AUDIO_LATENCY_FACTOR 1.25
> -#define MJPEG_VIDEO_DELAY_TH -15
> -
> -void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,
> -                                        uint32_t num_frames,
> -                                        uint32_t num_drops,
> -                                        uint32_t start_frame_mm_time,
> -                                        uint32_t end_frame_mm_time,
> -                                        int32_t end_frame_delay,
> -                                        uint32_t audio_delay)
> -{
> -    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> -    MJpegEncoderClientState *client_state = &rate_control->client_state;
> -    uint64_t avg_enc_size = 0;
> -    uint32_t min_playback_delay;
> -    int is_video_delay_small = FALSE;
> -
> -    spice_debug("client report: #frames %u, #drops %d, duration %u video-delay %d audio-delay %u",
> -                num_frames, num_drops,
> -                end_frame_mm_time - start_frame_mm_time,
> -                end_frame_delay, audio_delay);
> -
> -    if (!rate_control_is_active(encoder)) {
> -        spice_debug("rate control was not activated: ignoring");
> -        return;
> -    }
> -    if (rate_control->during_quality_eval) {
> -        if (rate_control->quality_eval_data.type == MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE &&
> -            rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) {
> -            spice_debug("during rate downgrade evaluation");
> -            return;
> -        }
> -    }
> -
> -    if (rate_control->num_recent_enc_frames) {
> -        avg_enc_size = rate_control->sum_recent_enc_size /
> -                       rate_control->num_recent_enc_frames;
> -    }
> -    spice_debug("recent size avg %.2f (KB)", avg_enc_size / 1024.0);
> -    min_playback_delay = get_min_required_playback_delay(avg_enc_size, rate_control->byte_rate,
> -                                                         mjpeg_encoder_get_latency(encoder));
> -    spice_debug("min-delay %u client-delay %d", min_playback_delay, end_frame_delay);
> -
> -    if (min_playback_delay > end_frame_delay) {
> -        uint32_t src_fps = mjpeg_encoder_get_source_fps(encoder);
> -        /*
> -        * if the stream is at its highest rate, we can't estimate the "real"
> -        * network bit rate and the min_playback_delay
> -        */
> -        if (rate_control->quality_id != MJPEG_QUALITY_SAMPLE_NUM - 1 ||
> -            rate_control->fps < MIN(src_fps, MJPEG_MAX_FPS) || end_frame_delay < 0) {
> -            is_video_delay_small = TRUE;
> -            if (encoder->cbs.update_client_playback_delay) {
> -                encoder->cbs.update_client_playback_delay(encoder->cbs_opaque,
> -                                                          min_playback_delay);
> -            }
> -        }
> -    }
> -
> -
> -    /*
> -     * If the audio latency has decreased (since the start of the current
> -     * sequence of positive reports), and the video latency is bigger, slow down
> -     * the video rate
> -     */
> -    if (end_frame_delay > 0 &&
> -        audio_delay < MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR*client_state->max_audio_latency &&
> -        end_frame_delay > MJPEG_VIDEO_VS_AUDIO_LATENCY_FACTOR*audio_delay) {
> -        spice_debug("video_latency >> audio_latency && audio_latency << max (%u)",
> -                    client_state->max_audio_latency);
> -        mjpeg_encoder_handle_negative_client_stream_report(encoder,
> -                                                           end_frame_mm_time);
> -        return;
> -    }
> -
> -    if (end_frame_delay < MJPEG_VIDEO_DELAY_TH) {
> -        mjpeg_encoder_handle_negative_client_stream_report(encoder,
> -                                                           end_frame_mm_time);
> -    } else {
> -        double major_delay_decrease_thresh;
> -        double medium_delay_decrease_thresh;
> -
> -        client_state->max_video_latency = MAX(end_frame_delay, client_state->max_video_latency);
> -        client_state->max_audio_latency = MAX(audio_delay, client_state->max_audio_latency);
> -
> -        medium_delay_decrease_thresh = client_state->max_video_latency;
> -        medium_delay_decrease_thresh *= MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR;
> -
> -        major_delay_decrease_thresh = medium_delay_decrease_thresh;
> -        major_delay_decrease_thresh *= MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR;
> -        /*
> -         * since the bit rate and the required latency are only evaluation based on the
> -         * reports we got till now, we assume that the latency is too low only if it
> -         * was higher during the time that passed since the last report that resulted
> -         * in a bit rate decrement. If we find that the latency has decreased, it might
> -         * suggest that the stream bit rate is too high.
> -         */
> -        if ((end_frame_delay < medium_delay_decrease_thresh &&
> -            is_video_delay_small) || end_frame_delay < major_delay_decrease_thresh) {
> -            spice_debug("downgrade due to short video delay (last=%u, past-max=%u",
> -                end_frame_delay, client_state->max_video_latency);
> -            mjpeg_encoder_handle_negative_client_stream_report(encoder,
> -                                                               end_frame_mm_time);
> -        } else if (!num_drops) {
> -            mjpeg_encoder_handle_positive_client_stream_report(encoder,
> -                                                               start_frame_mm_time);
> -
> -        }
> -    }
> -}
> -
> -void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder)
> -{
> -    encoder->rate_control.server_state.num_frames_dropped++;
> -    mjpeg_encoder_process_server_drops(encoder);
> -}
> -
> -/*
> - * decrease the bit rate if the drop rate on the sever side exceeds a pre defined
> - * threshold.
> - */
> -static void mjpeg_encoder_process_server_drops(MJpegEncoder *encoder)
> -{
> -    MJpegEncoderServerState *server_state = &encoder->rate_control.server_state;
> -    uint32_t num_frames_total;
> -    double drop_factor;
> -    uint32_t fps;
> -
> -    fps = MIN(encoder->rate_control.fps, mjpeg_encoder_get_source_fps(encoder));
> -    if (server_state->num_frames_encoded < fps * MJPEG_SERVER_STATUS_EVAL_FPS_INTERVAL) {
> -        return;
> -    }
> -
> -    num_frames_total = server_state->num_frames_dropped + server_state->num_frames_encoded;
> -    drop_factor = ((double)server_state->num_frames_dropped) / num_frames_total;
> -
> -    spice_debug("#drops %u total %u fps %u src-fps %u",
> -                server_state->num_frames_dropped,
> -                num_frames_total,
> -                encoder->rate_control.fps,
> -                mjpeg_encoder_get_source_fps(encoder));
> -
> -    if (drop_factor > MJPEG_SERVER_STATUS_DOWNGRADE_DROP_FACTOR_TH) {
> -        mjpeg_encoder_decrease_bit_rate(encoder);
> -    }
> -    server_state->num_frames_encoded = 0;
> -    server_state->num_frames_dropped = 0;
> -}
> -
> -uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder)
> -{
> -    return encoder->rate_control.byte_rate * 8;
> -}
> -
> -void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats *stats)
> -{
> -    spice_assert(encoder != NULL && stats != NULL);
> -    stats->starting_bit_rate = encoder->starting_bit_rate;
> -    stats->cur_bit_rate = mjpeg_encoder_get_bit_rate(encoder);
> -    stats->avg_quality = (double)encoder->avg_quality / encoder->num_frames;
> -}
> -
> -MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
> -                                MJpegEncoderRateControlCbs *cbs,
> -                                void *cbs_opaque)
> -{
> -    MJpegEncoder *encoder = spice_new0(MJpegEncoder, 1);
> -
> -    encoder->first_frame = TRUE;
> -    encoder->rate_control.byte_rate = starting_bit_rate / 8;
> -    encoder->starting_bit_rate = starting_bit_rate;
> -
> -    if (cbs) {
> -        struct timespec time;
> -
> -        clock_gettime(CLOCK_MONOTONIC, &time);
> -        encoder->cbs = *cbs;
> -        encoder->cbs_opaque = cbs_opaque;
> -        mjpeg_encoder_reset_quality(encoder, MJPEG_QUALITY_SAMPLE_NUM / 2, 5, 0);
> -        encoder->rate_control.during_quality_eval = TRUE;
> -        encoder->rate_control.quality_eval_data.type = MJPEG_QUALITY_EVAL_TYPE_SET;
> -        encoder->rate_control.quality_eval_data.reason = MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE;
> -        encoder->rate_control.warmup_start_time = ((uint64_t) time.tv_sec) * 1000000000 + time.tv_nsec;
> -    } else {
> -        encoder->cbs.get_roundtrip_ms = NULL;
> -        mjpeg_encoder_reset_quality(encoder, MJPEG_LEGACY_STATIC_QUALITY_ID, MJPEG_MAX_FPS, 0);
> -    }
> -
> -    encoder->cinfo.err = jpeg_std_error(&encoder->jerr);
> -    jpeg_create_compress(&encoder->cinfo);
> -
> -    return encoder;
> -}
> diff --git a/server/mjpeg_encoder.h b/server/mjpeg_encoder.h
> deleted file mode 100644
> index d070e70..0000000
> --- a/server/mjpeg_encoder.h
> +++ /dev/null
> @@ -1,100 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -
> -#ifndef _H_MJPEG_ENCODER
> -#define _H_MJPEG_ENCODER
> -
> -#include "red_common.h"
> -
> -enum {
> -    MJPEG_ENCODER_FRAME_UNSUPPORTED = -1,
> -    MJPEG_ENCODER_FRAME_DROP,
> -    MJPEG_ENCODER_FRAME_ENCODE_DONE,
> -};
> -
> -typedef struct MJpegEncoder MJpegEncoder;
> -
> -/*
> - * Callbacks required for controling and adjusting
> - * the stream bit rate:
> - * get_roundtrip_ms: roundtrip time in milliseconds
> - * get_source_fps: the input frame rate (#frames per second), i.e.,
> - * the rate of frames arriving from the guest to spice-server,
> - * before any drops.
> - */
> -typedef struct MJpegEncoderRateControlCbs {
> -    uint32_t (*get_roundtrip_ms)(void *opaque);
> -    uint32_t (*get_source_fps)(void *opaque);
> -    void (*update_client_playback_delay)(void *opaque, uint32_t delay_ms);
> -} MJpegEncoderRateControlCbs;
> -
> -typedef struct MJpegEncoderStats {
> -    uint64_t starting_bit_rate;
> -    uint64_t cur_bit_rate;
> -    double avg_quality;
> -} MJpegEncoderStats;
> -
> -MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
> -                                MJpegEncoderRateControlCbs *cbs, void *opaque);
> -void mjpeg_encoder_destroy(MJpegEncoder *encoder);
> -
> -int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
> -                               const SpiceBitmap *bitmap, int width, int height,
> -                               const SpiceRect *src,
> -                               int top_down, uint32_t frame_mm_time,
> -                               uint8_t **outbuf, size_t *outbuf_size,
> -                               int *data_size);
> -
> -/*
> - * bit rate control
> - */
> -
> -/*
> - * Data that should be periodically obtained from the client. The report contains:
> - * num_frames         : the number of frames that reached the client during the time
> - *                      the report is referring to.
> - * num_drops          : the part of the above frames that was dropped by the client due to
> - *                      late arrival time.
> - * start_frame_mm_time: the mm_time of the first frame included in the report
> - * end_frame_mm_time  : the mm_time of the last_frame included in the report
> - * end_frame_delay    : (end_frame_mm_time - client_mm_time)
> - * audio delay        : the latency of the audio playback.
> - *                      If there is no audio playback, set it to MAX_UINT.
> - *
> - */
> -void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,
> -                                        uint32_t num_frames,
> -                                        uint32_t num_drops,
> -                                        uint32_t start_frame_mm_time,
> -                                        uint32_t end_frame_mm_time,
> -                                        int32_t end_frame_delay,
> -                                        uint32_t audio_delay);
> -
> -/*
> - * Notify the encoder each time a frame is dropped due to pipe
> - * congestion.
> - * We can deduce the client state by the frame dropping rate in the server.
> - * Monitoring the frame drops can help in fine tuning the playback parameters
> - * when the client reports are delayed.
> - */
> -void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder);
> -
> -uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder);
> -void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats *stats);
> -
> -#endif
> diff --git a/server/red_channel.c b/server/red_channel.c
> index 609c83f..948d354 100644
> --- a/server/red_channel.c
> +++ b/server/red_channel.c
> @@ -42,7 +42,7 @@
>  #include "red_channel.h"
>  #include "reds.h"
>  #include "reds_stream.h"
> -#include "main_dispatcher.h"
> +#include "main-dispatcher.h"
>  #include "utils.h"
>
>  typedef struct EmptyMsgPipeItem {
> diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c
> index a7825f5..b8b77d7 100644
> --- a/server/red_dispatcher.c
> +++ b/server/red_dispatcher.c
> @@ -32,7 +32,7 @@
>
>  #include "spice.h"
>  #include "red_worker.h"
> -#include "reds_sw_canvas.h"
> +#include "sw-canvas.h"
>  #include "reds.h"
>  #include "dispatcher.h"
>  #include "red_parse_qxl.h"
> diff --git a/server/red_memslots.c b/server/red_memslots.c
> deleted file mode 100644
> index 0d2d963..0000000
> --- a/server/red_memslots.c
> +++ /dev/null
> @@ -1,184 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009,2010 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifdef HAVE_CONFIG_H
> -#include <config.h>
> -#endif
> -
> -#include <inttypes.h>
> -
> -#include "red_common.h"
> -#include "red_memslots.h"
> -
> -static unsigned long __get_clean_virt(RedMemSlotInfo *info, QXLPHYSICAL addr)
> -{
> -    return addr & info->memslot_clean_virt_mask;
> -}
> -
> -static void print_memslots(RedMemSlotInfo *info)
> -{
> -    int i;
> -    int x;
> -
> -    for (i = 0; i < info->num_memslots_groups; ++i) {
> -        for (x = 0; x < info->num_memslots; ++x) {
> -            if (!info->mem_slots[i][x].virt_start_addr &&
> -                !info->mem_slots[i][x].virt_end_addr) {
> -                continue;
> -            }
> -            printf("id %d, group %d, virt start %lx, virt end %lx, generation %u, delta %lx\n",
> -                   x, i, info->mem_slots[i][x].virt_start_addr,
> -                   info->mem_slots[i][x].virt_end_addr, info->mem_slots[i][x].generation,
> -                   info->mem_slots[i][x].address_delta);
> -            }
> -    }
> -}
> -
> -/* return 1 if validation successfull, 0 otherwise */
> -int memslot_validate_virt(RedMemSlotInfo *info, unsigned long virt, int slot_id,
> -                          uint32_t add_size, uint32_t group_id)
> -{
> -    MemSlot *slot;
> -
> -    slot = &info->mem_slots[group_id][slot_id];
> -    if ((virt + add_size) < virt) {
> -        spice_critical("virtual address overlap");
> -        return 0;
> -    }
> -
> -    if (virt < slot->virt_start_addr || (virt + add_size) > slot->virt_end_addr) {
> -        print_memslots(info);
> -        spice_critical("virtual address out of range\n"
> -              "    virt=0x%lx+0x%x slot_id=%d group_id=%d\n"
> -              "    slot=0x%lx-0x%lx delta=0x%lx",
> -              virt, add_size, slot_id, group_id,
> -              slot->virt_start_addr, slot->virt_end_addr, slot->address_delta);
> -        return 0;
> -    }
> -    return 1;
> -}
> -
> -/*
> - * return virtual address if successful, which may be 0.
> - * returns 0 and sets error to 1 if an error condition occurs.
> - */
> -unsigned long memslot_get_virt(RedMemSlotInfo *info, QXLPHYSICAL addr, uint32_t add_size,
> -                               int group_id, int *error)
> -{
> -    int slot_id;
> -    int generation;
> -    unsigned long h_virt;
> -
> -    MemSlot *slot;
> -
> -    *error = 0;
> -    if (group_id > info->num_memslots_groups) {
> -        spice_critical("group_id too big");
> -        *error = 1;
> -        return 0;
> -    }
> -
> -    slot_id = memslot_get_id(info, addr);
> -    if (slot_id > info->num_memslots) {
> -        print_memslots(info);
> -        spice_critical("slot_id %d too big, addr=%" PRIx64, slot_id, addr);
> -        *error = 1;
> -        return 0;
> -    }
> -
> -    slot = &info->mem_slots[group_id][slot_id];
> -
> -    generation = memslot_get_generation(info, addr);
> -    if (generation != slot->generation) {
> -        print_memslots(info);
> -        spice_critical("address generation is not valid, group_id %d, slot_id %d, gen %d, slot_gen %d\n",
> -              group_id, slot_id, generation, slot->generation);
> -        *error = 1;
> -        return 0;
> -    }
> -
> -    h_virt = __get_clean_virt(info, addr);
> -    h_virt += slot->address_delta;
> -
> -    if (!memslot_validate_virt(info, h_virt, slot_id, add_size, group_id)) {
> -        *error = 1;
> -        return 0;
> -    }
> -
> -    return h_virt;
> -}
> -
> -void memslot_info_init(RedMemSlotInfo *info,
> -                       uint32_t num_groups, uint32_t num_slots,
> -                       uint8_t generation_bits,
> -                       uint8_t id_bits,
> -                       uint8_t internal_groupslot_id)
> -{
> -    uint32_t i;
> -
> -    spice_return_if_fail(num_slots > 0);
> -    spice_return_if_fail(num_groups > 0);
> -
> -    info->num_memslots_groups = num_groups;
> -    info->num_memslots = num_slots;
> -    info->generation_bits = generation_bits;
> -    info->mem_slot_bits = id_bits;
> -    info->internal_groupslot_id = internal_groupslot_id;
> -
> -    info->mem_slots = spice_new(MemSlot *, num_groups);
> -
> -    for (i = 0; i < num_groups; ++i) {
> -        info->mem_slots[i] = spice_new0(MemSlot, num_slots);
> -    }
> -
> -    /* TODO: use QXLPHYSICAL_BITS */
> -    info->memslot_id_shift = 64 - info->mem_slot_bits;
> -    info->memslot_gen_shift = 64 - (info->mem_slot_bits + info->generation_bits);
> -    info->memslot_gen_mask = ~((QXLPHYSICAL)-1 << info->generation_bits);
> -    info->memslot_clean_virt_mask = (((QXLPHYSICAL)(-1)) >>
> -                                       (info->mem_slot_bits + info->generation_bits));
> -}
> -
> -void memslot_info_add_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id,
> -                           uint64_t addr_delta, unsigned long virt_start, unsigned long virt_end,
> -                           uint32_t generation)
> -{
> -    spice_return_if_fail(info->num_memslots_groups > slot_group_id);
> -    spice_return_if_fail(info->num_memslots > slot_id);
> -
> -    info->mem_slots[slot_group_id][slot_id].address_delta = addr_delta;
> -    info->mem_slots[slot_group_id][slot_id].virt_start_addr = virt_start;
> -    info->mem_slots[slot_group_id][slot_id].virt_end_addr = virt_end;
> -    info->mem_slots[slot_group_id][slot_id].generation = generation;
> -}
> -
> -void memslot_info_del_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id)
> -{
> -    spice_return_if_fail(info->num_memslots_groups > slot_group_id);
> -    spice_return_if_fail(info->num_memslots > slot_id);
> -
> -    info->mem_slots[slot_group_id][slot_id].virt_start_addr = 0;
> -    info->mem_slots[slot_group_id][slot_id].virt_end_addr = 0;
> -}
> -
> -void memslot_info_reset(RedMemSlotInfo *info)
> -{
> -        uint32_t i;
> -        for (i = 0; i < info->num_memslots_groups; ++i) {
> -            memset(info->mem_slots[i], 0, sizeof(MemSlot) * info->num_memslots);
> -        }
> -}
> diff --git a/server/red_memslots.h b/server/red_memslots.h
> deleted file mode 100644
> index a40050c..0000000
> --- a/server/red_memslots.h
> +++ /dev/null
> @@ -1,72 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009,2010 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -
> -#ifndef _H_REDMEMSLOTS
> -#define _H_REDMEMSLOTS
> -
> -#include "red_common.h"
> -
> -#include <spice/qxl_dev.h>
> -
> -typedef struct MemSlot {
> -    int generation;
> -    unsigned long virt_start_addr;
> -    unsigned long virt_end_addr;
> -    long address_delta;
> -} MemSlot;
> -
> -typedef struct RedMemSlotInfo {
> -    MemSlot **mem_slots;
> -    uint32_t num_memslots_groups;
> -    uint32_t num_memslots;
> -    uint8_t mem_slot_bits;
> -    uint8_t generation_bits;
> -    uint8_t memslot_id_shift;
> -    uint8_t memslot_gen_shift;
> -    uint8_t internal_groupslot_id;
> -    unsigned long memslot_gen_mask;
> -    unsigned long memslot_clean_virt_mask;
> -} RedMemSlotInfo;
> -
> -static inline int memslot_get_id(RedMemSlotInfo *info, uint64_t addr)
> -{
> -    return addr >> info->memslot_id_shift;
> -}
> -
> -static inline int memslot_get_generation(RedMemSlotInfo *info, uint64_t addr)
> -{
> -    return (addr >> info->memslot_gen_shift) & info->memslot_gen_mask;
> -}
> -
> -int memslot_validate_virt(RedMemSlotInfo *info, unsigned long virt, int slot_id,
> -                          uint32_t add_size, uint32_t group_id);
> -unsigned long memslot_get_virt(RedMemSlotInfo *info, QXLPHYSICAL addr, uint32_t add_size,
> -                       int group_id, int *error);
> -
> -void memslot_info_init(RedMemSlotInfo *info,
> -                       uint32_t num_groups, uint32_t num_slots,
> -                       uint8_t generation_bits,
> -                       uint8_t id_bits,
> -                       uint8_t internal_groupslot_id);
> -void memslot_info_add_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id,
> -                           uint64_t addr_delta, unsigned long virt_start, unsigned long virt_end,
> -                           uint32_t generation);
> -void memslot_info_del_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id);
> -void memslot_info_reset(RedMemSlotInfo *info);
> -
> -#endif
> diff --git a/server/red_parse_qxl.c b/server/red_parse_qxl.c
> index 6e2ca99..522a915 100644
> --- a/server/red_parse_qxl.c
> +++ b/server/red_parse_qxl.c
> @@ -25,7 +25,7 @@
>  #include "common/lz_common.h"
>  #include "spice-bitmap-utils.h"
>  #include "red_common.h"
> -#include "red_memslots.h"
> +#include "memslot.h"
>  #include "red_parse_qxl.h"
>
>  /* Max size in bytes for any data field used in a QXL command.
> diff --git a/server/red_parse_qxl.h b/server/red_parse_qxl.h
> index b3b28e1..09059f5 100644
> --- a/server/red_parse_qxl.h
> +++ b/server/red_parse_qxl.h
> @@ -21,7 +21,7 @@
>
>  #include <spice/qxl_dev.h>
>  #include "red_common.h"
> -#include "red_memslots.h"
> +#include "memslot.h"
>
>  typedef struct RedDrawable {
>      int refs;
> diff --git a/server/red_record_qxl.c b/server/red_record_qxl.c
> index 17f17bd..52c0e81 100644
> --- a/server/red_record_qxl.c
> +++ b/server/red_record_qxl.c
> @@ -23,9 +23,9 @@
>  #include <inttypes.h>
>  #include "red_worker.h"
>  #include "red_common.h"
> -#include "red_memslots.h"
> +#include "memslot.h"
>  #include "red_parse_qxl.h"
> -#include "zlib_encoder.h"
> +#include "zlib-encoder.h"
>
>  #if 0
>  static void hexdump_qxl(RedMemSlotInfo *slots, int group_id,
> diff --git a/server/red_record_qxl.h b/server/red_record_qxl.h
> index b737db8..6fcbec9 100644
> --- a/server/red_record_qxl.h
> +++ b/server/red_record_qxl.h
> @@ -21,7 +21,7 @@
>
>  #include <spice/qxl_dev.h>
>  #include "red_common.h"
> -#include "red_memslots.h"
> +#include "memslot.h"
>
>  void red_record_dev_input_primary_surface_create(
>                             FILE *fd, QXLDevSurfaceCreate *surface, uint8_t *line_0);
> diff --git a/server/red_replay_qxl.c b/server/red_replay_qxl.c
> index ad1a8fd..6e32588 100644
> --- a/server/red_replay_qxl.c
> +++ b/server/red_replay_qxl.c
> @@ -26,7 +26,7 @@
>  #include "reds.h"
>  #include "red_worker.h"
>  #include "red_common.h"
> -#include "red_memslots.h"
> +#include "memslot.h"
>  #include "red_parse_qxl.h"
>  #include "red_replay_qxl.h"
>  #include <glib.h>
> diff --git a/server/reds.c b/server/reds.c
> index 8b3c3cb..5891034 100644
> --- a/server/reds.c
> +++ b/server/reds.c
> @@ -56,16 +56,16 @@
>  #include "spice.h"
>  #include "reds.h"
>  #include "agent-msg-filter.h"
> -#include "inputs_channel.h"
> -#include "main_channel.h"
> +#include "inputs-channel.h"
> +#include "main-channel.h"
>  #include "red_common.h"
>  #include "red_dispatcher.h"
> -#include "main_dispatcher.h"
> -#include "snd_worker.h"
> +#include "main-dispatcher.h"
> +#include "sound.h"
>  #include "stat.h"
>  #include "demarshallers.h"
> -#include "char_device.h"
> -#include "migration_protocol.h"
> +#include "char-device.h"
> +#include "migration-protocol.h"
>  #ifdef USE_SMARTCARD
>  #include "smartcard.h"
>  #endif
> diff --git a/server/reds.h b/server/reds.h
> index fcdc5eb..0584694 100644
> --- a/server/reds.h
> +++ b/server/reds.h
> @@ -28,7 +28,7 @@
>  #include "common/messages.h"
>  #include "spice.h"
>  #include "red_channel.h"
> -#include "migration_protocol.h"
> +#include "migration-protocol.h"
>
>  struct QXLState {
>      QXLInterface          *qif;
> @@ -75,7 +75,7 @@ extern SpiceImageCompression image_compression;
>  extern spice_wan_compression_t jpeg_state;
>  extern spice_wan_compression_t zlib_glz_state;
>
> -// Temporary measures to make splitting reds.c to inputs_channel.c easier
> +// Temporary measures to make splitting reds.c to inputs-channel.c easier
>
>  /* should be called only from main_dispatcher */
>  void reds_client_disconnect(RedClient *client);
> diff --git a/server/reds_stream.c b/server/reds_stream.c
> index 3b47391..6f5c43f 100644
> --- a/server/reds_stream.c
> +++ b/server/reds_stream.c
> @@ -19,7 +19,7 @@
>  #include <config.h>
>  #endif
>
> -#include "main_dispatcher.h"
> +#include "main-dispatcher.h"
>  #include "red_common.h"
>  #include "reds_stream.h"
>  #include "common/log.h"
> diff --git a/server/reds_sw_canvas.c b/server/reds_sw_canvas.c
> deleted file mode 100644
> index 297df37..0000000
> --- a/server/reds_sw_canvas.c
> +++ /dev/null
> @@ -1,26 +0,0 @@
> -/*
> -   Copyright (C) 2011 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifdef HAVE_CONFIG_H
> -#include <config.h>
> -#endif
> -
> -#include "common/spice_common.h"
> -
> -#include "reds_sw_canvas.h"
> -#define SW_CANVAS_IMAGE_CACHE
> -#include "common/sw_canvas.c"
> -#undef SW_CANVAS_IMAGE_CACHE
> diff --git a/server/reds_sw_canvas.h b/server/reds_sw_canvas.h
> deleted file mode 100644
> index 96a4c0c..0000000
> --- a/server/reds_sw_canvas.h
> +++ /dev/null
> @@ -1,24 +0,0 @@
> -/*
> -   Copyright (C) 2011 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifndef _H_REDS_SW_CANVAS
> -#define _H_REDS_SW_CANVAS
> -
> -#define SW_CANVAS_IMAGE_CACHE
> -#include "common/sw_canvas.h"
> -#undef SW_CANVAS_IMAGE_CACHE
> -
> -#endif
> diff --git a/server/smartcard.c b/server/smartcard.c
> index aad22aa..928e27b8 100644
> --- a/server/smartcard.c
> +++ b/server/smartcard.c
> @@ -23,10 +23,10 @@
>  #include <vscard_common.h>
>
>  #include "reds.h"
> -#include "char_device.h"
> +#include "char-device.h"
>  #include "red_channel.h"
>  #include "smartcard.h"
> -#include "migration_protocol.h"
> +#include "migration-protocol.h"
>
>  /*
>   * TODO: the code doesn't really support multiple readers.
> diff --git a/server/snd_worker.c b/server/snd_worker.c
> deleted file mode 100644
> index b039939..0000000
> --- a/server/snd_worker.c
> +++ /dev/null
> @@ -1,1625 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifdef HAVE_CONFIG_H
> -#include <config.h>
> -#endif
> -
> -#include <fcntl.h>
> -#include <errno.h>
> -#include <limits.h>
> -#include <sys/socket.h>
> -#include <netinet/ip.h>
> -#include <netinet/tcp.h>
> -
> -#include "common/marshaller.h"
> -#include "common/generated_server_marshallers.h"
> -
> -#include "spice.h"
> -#include "red_common.h"
> -#include "main_channel.h"
> -#include "reds.h"
> -#include "red_dispatcher.h"
> -#include "snd_worker.h"
> -#include "common/snd_codec.h"
> -#include "demarshallers.h"
> -
> -#ifndef IOV_MAX
> -#define IOV_MAX 1024
> -#endif
> -
> -#define SND_RECEIVE_BUF_SIZE     (16 * 1024 * 2)
> -#define RECORD_SAMPLES_SIZE (SND_RECEIVE_BUF_SIZE >> 2)
> -
> -enum PlaybackCommand {
> -    SND_PLAYBACK_MIGRATE,
> -    SND_PLAYBACK_MODE,
> -    SND_PLAYBACK_CTRL,
> -    SND_PLAYBACK_PCM,
> -    SND_PLAYBACK_VOLUME,
> -    SND_PLAYBACK_LATENCY,
> -};
> -
> -enum RecordCommand {
> -    SND_RECORD_MIGRATE,
> -    SND_RECORD_CTRL,
> -    SND_RECORD_VOLUME,
> -};
> -
> -#define SND_PLAYBACK_MIGRATE_MASK (1 << SND_PLAYBACK_MIGRATE)
> -#define SND_PLAYBACK_MODE_MASK (1 << SND_PLAYBACK_MODE)
> -#define SND_PLAYBACK_CTRL_MASK (1 << SND_PLAYBACK_CTRL)
> -#define SND_PLAYBACK_PCM_MASK (1 << SND_PLAYBACK_PCM)
> -#define SND_PLAYBACK_VOLUME_MASK (1 << SND_PLAYBACK_VOLUME)
> -#define SND_PLAYBACK_LATENCY_MASK ( 1 << SND_PLAYBACK_LATENCY)
> -
> -#define SND_RECORD_MIGRATE_MASK (1 << SND_RECORD_MIGRATE)
> -#define SND_RECORD_CTRL_MASK (1 << SND_RECORD_CTRL)
> -#define SND_RECORD_VOLUME_MASK (1 << SND_RECORD_VOLUME)
> -
> -typedef struct SndChannel SndChannel;
> -typedef void (*snd_channel_send_messages_proc)(void *in_channel);
> -typedef int (*snd_channel_handle_message_proc)(SndChannel *channel, size_t size, uint32_t type, void *message);
> -typedef void (*snd_channel_on_message_done_proc)(SndChannel *channel);
> -typedef void (*snd_channel_cleanup_channel_proc)(SndChannel *channel);
> -
> -typedef struct SndWorker SndWorker;
> -
> -struct SndChannel {
> -    RedsStream *stream;
> -    SndWorker *worker;
> -    spice_parse_channel_func_t parser;
> -    int refs;
> -
> -    RedChannelClient *channel_client;
> -
> -    int active;
> -    int client_active;
> -    int blocked;
> -
> -    uint32_t command;
> -    uint32_t ack_generation;
> -    uint32_t client_ack_generation;
> -    uint32_t out_messages;
> -    uint32_t ack_messages;
> -
> -    struct {
> -        uint64_t serial;
> -        SpiceMarshaller *marshaller;
> -        uint32_t size;
> -        uint32_t pos;
> -    } send_data;
> -
> -    struct {
> -        uint8_t buf[SND_RECEIVE_BUF_SIZE];
> -        uint8_t *message_start;
> -        uint8_t *now;
> -        uint8_t *end;
> -    } receive_data;
> -
> -    snd_channel_send_messages_proc send_messages;
> -    snd_channel_handle_message_proc handle_message;
> -    snd_channel_on_message_done_proc on_message_done;
> -    snd_channel_cleanup_channel_proc cleanup;
> -};
> -
> -typedef struct PlaybackChannel PlaybackChannel;
> -
> -typedef struct AudioFrame AudioFrame;
> -struct AudioFrame {
> -    uint32_t time;
> -    uint32_t samples[SND_CODEC_MAX_FRAME_SIZE];
> -    PlaybackChannel *channel;
> -    AudioFrame *next;
> -};
> -
> -struct PlaybackChannel {
> -    SndChannel base;
> -    AudioFrame frames[3];
> -    AudioFrame *free_frames;
> -    AudioFrame *in_progress;
> -    AudioFrame *pending_frame;
> -    uint32_t mode;
> -    uint32_t latency;
> -    SndCodec codec;
> -    uint8_t  encode_buf[SND_CODEC_MAX_COMPRESSED_BYTES];
> -};
> -
> -struct SndWorker {
> -    RedChannel *base_channel;
> -    SndChannel *connection;
> -    SndWorker *next;
> -    int active;
> -};
> -
> -typedef struct SpiceVolumeState {
> -    uint8_t volume_nchannels;
> -    uint16_t *volume;
> -    int mute;
> -} SpiceVolumeState;
> -
> -struct SpicePlaybackState {
> -    struct SndWorker worker;
> -    SpicePlaybackInstance *sin;
> -    SpiceVolumeState volume;
> -    uint32_t frequency;
> -};
> -
> -struct SpiceRecordState {
> -    struct SndWorker worker;
> -    SpiceRecordInstance *sin;
> -    SpiceVolumeState volume;
> -    uint32_t frequency;
> -};
> -
> -typedef struct RecordChannel {
> -    SndChannel base;
> -    uint32_t samples[RECORD_SAMPLES_SIZE];
> -    uint32_t write_pos;
> -    uint32_t read_pos;
> -    uint32_t mode;
> -    uint32_t mode_time;
> -    uint32_t start_time;
> -    SndCodec codec;
> -    uint8_t  decode_buf[SND_CODEC_MAX_FRAME_BYTES];
> -} RecordChannel;
> -
> -static SndWorker *workers;
> -static uint32_t playback_compression = TRUE;
> -
> -static void snd_receive(void* data);
> -
> -static SndChannel *snd_channel_get(SndChannel *channel)
> -{
> -    channel->refs++;
> -    return channel;
> -}
> -
> -static SndChannel *snd_channel_put(SndChannel *channel)
> -{
> -    if (!--channel->refs) {
> -        spice_printerr("SndChannel=%p freed", channel);
> -        free(channel);
> -        return NULL;
> -    }
> -    return channel;
> -}
> -
> -static void snd_disconnect_channel(SndChannel *channel)
> -{
> -    SndWorker *worker;
> -
> -    if (!channel || !channel->stream) {
> -        spice_debug("not connected");
> -        return;
> -    }
> -    spice_debug("SndChannel=%p rcc=%p type=%d",
> -                 channel, channel->channel_client, channel->channel_client->channel->type);
> -    worker = channel->worker;
> -    channel->cleanup(channel);
> -    red_channel_client_disconnect(worker->connection->channel_client);
> -    worker->connection->channel_client = NULL;
> -    core->watch_remove(channel->stream->watch);
> -    channel->stream->watch = NULL;
> -    reds_stream_free(channel->stream);
> -    channel->stream = NULL;
> -    spice_marshaller_destroy(channel->send_data.marshaller);
> -    snd_channel_put(channel);
> -    worker->connection = NULL;
> -}
> -
> -static void snd_playback_free_frame(PlaybackChannel *playback_channel, AudioFrame *frame)
> -{
> -    frame->channel = playback_channel;
> -    frame->next = playback_channel->free_frames;
> -    playback_channel->free_frames = frame;
> -}
> -
> -static void snd_playback_on_message_done(SndChannel *channel)
> -{
> -    PlaybackChannel *playback_channel = (PlaybackChannel *)channel;
> -    if (playback_channel->in_progress) {
> -        snd_playback_free_frame(playback_channel, playback_channel->in_progress);
> -        playback_channel->in_progress = NULL;
> -        if (playback_channel->pending_frame) {
> -            channel->command |= SND_PLAYBACK_PCM_MASK;
> -        }
> -    }
> -}
> -
> -static void snd_record_on_message_done(SndChannel *channel)
> -{
> -}
> -
> -static int snd_send_data(SndChannel *channel)
> -{
> -    uint32_t n;
> -
> -    if (!channel) {
> -        return FALSE;
> -    }
> -
> -    if (!(n = channel->send_data.size - channel->send_data.pos)) {
> -        return TRUE;
> -    }
> -
> -    for (;;) {
> -        struct iovec vec[IOV_MAX];
> -        int vec_size;
> -
> -        if (!n) {
> -            channel->on_message_done(channel);
> -
> -            if (channel->blocked) {
> -                channel->blocked = FALSE;
> -                core->watch_update_mask(channel->stream->watch, SPICE_WATCH_EVENT_READ);
> -            }
> -            break;
> -        }
> -
> -        vec_size = spice_marshaller_fill_iovec(channel->send_data.marshaller,
> -                                               vec, IOV_MAX, channel->send_data.pos);
> -        n = reds_stream_writev(channel->stream, vec, vec_size);
> -        if (n == -1) {
> -            switch (errno) {
> -            case EAGAIN:
> -                channel->blocked = TRUE;
> -                core->watch_update_mask(channel->stream->watch, SPICE_WATCH_EVENT_READ |
> -                                        SPICE_WATCH_EVENT_WRITE);
> -                return FALSE;
> -            case EINTR:
> -                break;
> -            case EPIPE:
> -                snd_disconnect_channel(channel);
> -                return FALSE;
> -            default:
> -                spice_printerr("%s", strerror(errno));
> -                snd_disconnect_channel(channel);
> -                return FALSE;
> -            }
> -        } else {
> -            channel->send_data.pos += n;
> -        }
> -        n = channel->send_data.size - channel->send_data.pos;
> -    }
> -    return TRUE;
> -}
> -
> -static int snd_record_handle_write(RecordChannel *record_channel, size_t size, void *message)
> -{
> -    SpiceMsgcRecordPacket *packet;
> -    uint32_t write_pos;
> -    uint32_t* data;
> -    uint32_t len;
> -    uint32_t now;
> -
> -    if (!record_channel) {
> -        return FALSE;
> -    }
> -
> -    packet = (SpiceMsgcRecordPacket *)message;
> -
> -    if (record_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
> -        data = (uint32_t *)packet->data;
> -        size = packet->data_size >> 2;
> -        size = MIN(size, RECORD_SAMPLES_SIZE);
> -     } else {
> -        int decode_size;
> -        decode_size = sizeof(record_channel->decode_buf);
> -        if (snd_codec_decode(record_channel->codec, packet->data, packet->data_size,
> -                    record_channel->decode_buf, &decode_size) != SND_CODEC_OK)
> -            return FALSE;
> -        data = (uint32_t *) record_channel->decode_buf;
> -        size = decode_size >> 2;
> -    }
> -
> -    write_pos = record_channel->write_pos % RECORD_SAMPLES_SIZE;
> -    record_channel->write_pos += size;
> -    len = RECORD_SAMPLES_SIZE - write_pos;
> -    now = MIN(len, size);
> -    size -= now;
> -    memcpy(record_channel->samples + write_pos, data, now << 2);
> -
> -    if (size) {
> -        memcpy(record_channel->samples, data + now, size << 2);
> -    }
> -
> -    if (record_channel->write_pos - record_channel->read_pos > RECORD_SAMPLES_SIZE) {
> -        record_channel->read_pos = record_channel->write_pos - RECORD_SAMPLES_SIZE;
> -    }
> -    return TRUE;
> -}
> -
> -static int snd_playback_handle_message(SndChannel *channel, size_t size, uint32_t type, void *message)
> -{
> -    if (!channel) {
> -        return FALSE;
> -    }
> -
> -    switch (type) {
> -    case SPICE_MSGC_DISCONNECTING:
> -        break;
> -    default:
> -        spice_printerr("invalid message type %u", type);
> -        return FALSE;
> -    }
> -    return TRUE;
> -}
> -
> -static int snd_record_handle_message(SndChannel *channel, size_t size, uint32_t type, void *message)
> -{
> -    RecordChannel *record_channel = (RecordChannel *)channel;
> -
> -    if (!channel) {
> -        return FALSE;
> -    }
> -    switch (type) {
> -    case SPICE_MSGC_RECORD_DATA:
> -        return snd_record_handle_write((RecordChannel *)channel, size, message);
> -    case SPICE_MSGC_RECORD_MODE: {
> -        SpiceMsgcRecordMode *mode = (SpiceMsgcRecordMode *)message;
> -        SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
> -        record_channel->mode_time = mode->time;
> -        if (mode->mode != SPICE_AUDIO_DATA_MODE_RAW) {
> -            if (snd_codec_is_capable(mode->mode, st->frequency)) {
> -                if (snd_codec_create(&record_channel->codec, mode->mode, st->frequency, SND_CODEC_DECODE) == SND_CODEC_OK) {
> -                    record_channel->mode = mode->mode;
> -                } else {
> -                    spice_printerr("create decoder failed");
> -                    return FALSE;
> -                }
> -            }
> -            else {
> -                spice_printerr("unsupported mode %d", record_channel->mode);
> -                return FALSE;
> -            }
> -        }
> -        else
> -            record_channel->mode = mode->mode;
> -        break;
> -    }
> -
> -    case SPICE_MSGC_RECORD_START_MARK: {
> -        SpiceMsgcRecordStartMark *mark = (SpiceMsgcRecordStartMark *)message;
> -        record_channel->start_time = mark->time;
> -        break;
> -    }
> -    case SPICE_MSGC_DISCONNECTING:
> -        break;
> -    default:
> -        spice_printerr("invalid message type %u", type);
> -        return FALSE;
> -    }
> -    return TRUE;
> -}
> -
> -static void snd_receive(void* data)
> -{
> -    SndChannel *channel = (SndChannel*)data;
> -    SpiceDataHeaderOpaque *header;
> -
> -    if (!channel) {
> -        return;
> -    }
> -
> -    header = &channel->channel_client->incoming.header;
> -
> -    for (;;) {
> -        ssize_t n;
> -        n = channel->receive_data.end - channel->receive_data.now;
> -        spice_warn_if(n <= 0);
> -        n = reds_stream_read(channel->stream, channel->receive_data.now, n);
> -        if (n <= 0) {
> -            if (n == 0) {
> -                snd_disconnect_channel(channel);
> -                return;
> -            }
> -            spice_assert(n == -1);
> -            switch (errno) {
> -            case EAGAIN:
> -                return;
> -            case EINTR:
> -                break;
> -            case EPIPE:
> -                snd_disconnect_channel(channel);
> -                return;
> -            default:
> -                spice_printerr("%s", strerror(errno));
> -                snd_disconnect_channel(channel);
> -                return;
> -            }
> -        } else {
> -            channel->receive_data.now += n;
> -            for (;;) {
> -                uint8_t *msg_start = channel->receive_data.message_start;
> -                uint8_t *data = msg_start + header->header_size;
> -                size_t parsed_size;
> -                uint8_t *parsed;
> -                message_destructor_t parsed_free;
> -
> -                header->data = msg_start;
> -                n = channel->receive_data.now - msg_start;
> -
> -                if (n < header->header_size ||
> -                    n < header->header_size + header->get_msg_size(header)) {
> -                    break;
> -                }
> -                parsed = channel->parser((void *)data, data + header->get_msg_size(header),
> -                                         header->get_msg_type(header),
> -                                         SPICE_VERSION_MINOR, &parsed_size, &parsed_free);
> -                if (parsed == NULL) {
> -                    spice_printerr("failed to parse message type %d", header->get_msg_type(header));
> -                    snd_disconnect_channel(channel);
> -                    return;
> -                }
> -                if (!channel->handle_message(channel, parsed_size,
> -                                             header->get_msg_type(header), parsed)) {
> -                    free(parsed);
> -                    snd_disconnect_channel(channel);
> -                    return;
> -                }
> -                parsed_free(parsed);
> -                channel->receive_data.message_start = msg_start + header->header_size +
> -                                                     header->get_msg_size(header);
> -            }
> -            if (channel->receive_data.now == channel->receive_data.message_start) {
> -                channel->receive_data.now = channel->receive_data.buf;
> -                channel->receive_data.message_start = channel->receive_data.buf;
> -            } else if (channel->receive_data.now == channel->receive_data.end) {
> -                memcpy(channel->receive_data.buf, channel->receive_data.message_start, n);
> -                channel->receive_data.now = channel->receive_data.buf + n;
> -                channel->receive_data.message_start = channel->receive_data.buf;
> -            }
> -        }
> -    }
> -}
> -
> -static void snd_event(int fd, int event, void *data)
> -{
> -    SndChannel *channel = data;
> -
> -    if (event & SPICE_WATCH_EVENT_READ) {
> -        snd_receive(channel);
> -    }
> -    if (event & SPICE_WATCH_EVENT_WRITE) {
> -        channel->send_messages(channel);
> -    }
> -}
> -
> -static inline int snd_reset_send_data(SndChannel *channel, uint16_t verb)
> -{
> -    SpiceDataHeaderOpaque *header;
> -
> -    if (!channel) {
> -        return FALSE;
> -    }
> -
> -    header = &channel->channel_client->send_data.header;
> -    spice_marshaller_reset(channel->send_data.marshaller);
> -    header->data = spice_marshaller_reserve_space(channel->send_data.marshaller,
> -                                                  header->header_size);
> -    spice_marshaller_set_base(channel->send_data.marshaller,
> -                              header->header_size);
> -    channel->send_data.pos = 0;
> -    header->set_msg_size(header, 0);
> -    header->set_msg_type(header, verb);
> -    channel->send_data.serial++;
> -    if (!channel->channel_client->is_mini_header) {
> -        header->set_msg_serial(header, channel->send_data.serial);
> -        header->set_msg_sub_list(header, 0);
> -    }
> -
> -    return TRUE;
> -}
> -
> -static int snd_begin_send_message(SndChannel *channel)
> -{
> -    SpiceDataHeaderOpaque *header = &channel->channel_client->send_data.header;
> -
> -    spice_marshaller_flush(channel->send_data.marshaller);
> -    channel->send_data.size = spice_marshaller_get_total_size(channel->send_data.marshaller);
> -    header->set_msg_size(header, channel->send_data.size - header->header_size);
> -    return snd_send_data(channel);
> -}
> -
> -static int snd_channel_send_migrate(SndChannel *channel)
> -{
> -    SpiceMsgMigrate migrate;
> -
> -    if (!snd_reset_send_data(channel, SPICE_MSG_MIGRATE)) {
> -        return FALSE;
> -    }
> -    spice_debug(NULL);
> -    migrate.flags = 0;
> -    spice_marshall_msg_migrate(channel->send_data.marshaller, &migrate);
> -
> -    return snd_begin_send_message(channel);
> -}
> -
> -static int snd_playback_send_migrate(PlaybackChannel *channel)
> -{
> -    return snd_channel_send_migrate(&channel->base);
> -}
> -
> -static int snd_send_volume(SndChannel *channel, SpiceVolumeState *st, int msg)
> -{
> -    SpiceMsgAudioVolume *vol;
> -    uint8_t c;
> -
> -    vol = alloca(sizeof (SpiceMsgAudioVolume) +
> -                 st->volume_nchannels * sizeof (uint16_t));
> -    if (!snd_reset_send_data(channel, msg)) {
> -        return FALSE;
> -    }
> -    vol->nchannels = st->volume_nchannels;
> -    for (c = 0; c < st->volume_nchannels; ++c) {
> -        vol->volume[c] = st->volume[c];
> -    }
> -    spice_marshall_SpiceMsgAudioVolume(channel->send_data.marshaller, vol);
> -
> -    return snd_begin_send_message(channel);
> -}
> -
> -static int snd_playback_send_volume(PlaybackChannel *playback_channel)
> -{
> -    SndChannel *channel = &playback_channel->base;
> -    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
> -
> -    if (!red_channel_client_test_remote_cap(channel->channel_client,
> -                                            SPICE_PLAYBACK_CAP_VOLUME)) {
> -        return TRUE;
> -    }
> -
> -    return snd_send_volume(channel, &st->volume, SPICE_MSG_PLAYBACK_VOLUME);
> -}
> -
> -static int snd_send_mute(SndChannel *channel, SpiceVolumeState *st, int msg)
> -{
> -    SpiceMsgAudioMute mute;
> -
> -    if (!snd_reset_send_data(channel, msg)) {
> -        return FALSE;
> -    }
> -    mute.mute = st->mute;
> -    spice_marshall_SpiceMsgAudioMute(channel->send_data.marshaller, &mute);
> -
> -    return snd_begin_send_message(channel);
> -}
> -
> -static int snd_playback_send_mute(PlaybackChannel *playback_channel)
> -{
> -    SndChannel *channel = &playback_channel->base;
> -    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
> -
> -    if (!red_channel_client_test_remote_cap(channel->channel_client,
> -                                            SPICE_PLAYBACK_CAP_VOLUME)) {
> -        return TRUE;
> -    }
> -
> -    return snd_send_mute(channel, &st->volume, SPICE_MSG_PLAYBACK_MUTE);
> -}
> -
> -static int snd_playback_send_latency(PlaybackChannel *playback_channel)
> -{
> -    SndChannel *channel = &playback_channel->base;
> -    SpiceMsgPlaybackLatency latency_msg;
> -
> -    spice_debug("latency %u", playback_channel->latency);
> -    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_LATENCY)) {
> -        return FALSE;
> -    }
> -    latency_msg.latency_ms = playback_channel->latency;
> -    spice_marshall_msg_playback_latency(channel->send_data.marshaller, &latency_msg);
> -
> -    return snd_begin_send_message(channel);
> -}
> -static int snd_playback_send_start(PlaybackChannel *playback_channel)
> -{
> -    SndChannel *channel = (SndChannel *)playback_channel;
> -    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
> -    SpiceMsgPlaybackStart start;
> -
> -    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_START)) {
> -        return FALSE;
> -    }
> -
> -    start.channels = SPICE_INTERFACE_PLAYBACK_CHAN;
> -    start.frequency = st->frequency;
> -    spice_assert(SPICE_INTERFACE_PLAYBACK_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
> -    start.format = SPICE_AUDIO_FMT_S16;
> -    start.time = reds_get_mm_time();
> -    spice_marshall_msg_playback_start(channel->send_data.marshaller, &start);
> -
> -    return snd_begin_send_message(channel);
> -}
> -
> -static int snd_playback_send_stop(PlaybackChannel *playback_channel)
> -{
> -    SndChannel *channel = (SndChannel *)playback_channel;
> -
> -    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_STOP)) {
> -        return FALSE;
> -    }
> -
> -    return snd_begin_send_message(channel);
> -}
> -
> -static int snd_playback_send_ctl(PlaybackChannel *playback_channel)
> -{
> -    SndChannel *channel = (SndChannel *)playback_channel;
> -
> -    if ((channel->client_active = channel->active)) {
> -        return snd_playback_send_start(playback_channel);
> -    } else {
> -        return snd_playback_send_stop(playback_channel);
> -    }
> -}
> -
> -static int snd_record_send_start(RecordChannel *record_channel)
> -{
> -    SndChannel *channel = (SndChannel *)record_channel;
> -    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
> -    SpiceMsgRecordStart start;
> -
> -    if (!snd_reset_send_data(channel, SPICE_MSG_RECORD_START)) {
> -        return FALSE;
> -    }
> -
> -    start.channels = SPICE_INTERFACE_RECORD_CHAN;
> -    start.frequency = st->frequency;
> -    spice_assert(SPICE_INTERFACE_RECORD_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
> -    start.format = SPICE_AUDIO_FMT_S16;
> -    spice_marshall_msg_record_start(channel->send_data.marshaller, &start);
> -
> -    return snd_begin_send_message(channel);
> -}
> -
> -static int snd_record_send_stop(RecordChannel *record_channel)
> -{
> -    SndChannel *channel = (SndChannel *)record_channel;
> -
> -    if (!snd_reset_send_data(channel, SPICE_MSG_RECORD_STOP)) {
> -        return FALSE;
> -    }
> -
> -    return snd_begin_send_message(channel);
> -}
> -
> -static int snd_record_send_ctl(RecordChannel *record_channel)
> -{
> -    SndChannel *channel = (SndChannel *)record_channel;
> -
> -    if ((channel->client_active = channel->active)) {
> -        return snd_record_send_start(record_channel);
> -    } else {
> -        return snd_record_send_stop(record_channel);
> -    }
> -}
> -
> -static int snd_record_send_volume(RecordChannel *record_channel)
> -{
> -    SndChannel *channel = &record_channel->base;
> -    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
> -
> -    if (!red_channel_client_test_remote_cap(channel->channel_client,
> -                                            SPICE_RECORD_CAP_VOLUME)) {
> -        return TRUE;
> -    }
> -
> -    return snd_send_volume(channel, &st->volume, SPICE_MSG_RECORD_VOLUME);
> -}
> -
> -static int snd_record_send_mute(RecordChannel *record_channel)
> -{
> -    SndChannel *channel = &record_channel->base;
> -    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
> -
> -    if (!red_channel_client_test_remote_cap(channel->channel_client,
> -                                            SPICE_RECORD_CAP_VOLUME)) {
> -        return TRUE;
> -    }
> -
> -    return snd_send_mute(channel, &st->volume, SPICE_MSG_RECORD_MUTE);
> -}
> -
> -static int snd_record_send_migrate(RecordChannel *record_channel)
> -{
> -    /* No need for migration data: if recording has started before migration,
> -     * the client receives RECORD_STOP from the src before the migration completion
> -     * notification (when the vm is stopped).
> -     * Afterwards, when the vm starts on the dest, the client receives RECORD_START. */
> -    return snd_channel_send_migrate(&record_channel->base);
> -}
> -
> -static int snd_playback_send_write(PlaybackChannel *playback_channel)
> -{
> -    SndChannel *channel = (SndChannel *)playback_channel;
> -    AudioFrame *frame;
> -    SpiceMsgPlaybackPacket msg;
> -
> -    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_DATA)) {
> -        return FALSE;
> -    }
> -
> -    frame = playback_channel->in_progress;
> -    msg.time = frame->time;
> -
> -    spice_marshall_msg_playback_data(channel->send_data.marshaller, &msg);
> -
> -    if (playback_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
> -        spice_marshaller_add_ref(channel->send_data.marshaller,
> -                                 (uint8_t *)frame->samples,
> -                                 snd_codec_frame_size(playback_channel->codec) * sizeof(frame->samples[0]));
> -    }
> -    else {
> -        int n = sizeof(playback_channel->encode_buf);
> -        if (snd_codec_encode(playback_channel->codec, (uint8_t *) frame->samples,
> -                                    snd_codec_frame_size(playback_channel->codec) * sizeof(frame->samples[0]),
> -                                    playback_channel->encode_buf, &n) != SND_CODEC_OK) {
> -            spice_printerr("encode failed");
> -            snd_disconnect_channel(channel);
> -            return FALSE;
> -        }
> -        spice_marshaller_add_ref(channel->send_data.marshaller, playback_channel->encode_buf, n);
> -    }
> -
> -    return snd_begin_send_message(channel);
> -}
> -
> -static int playback_send_mode(PlaybackChannel *playback_channel)
> -{
> -    SndChannel *channel = (SndChannel *)playback_channel;
> -    SpiceMsgPlaybackMode mode;
> -
> -    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_MODE)) {
> -        return FALSE;
> -    }
> -    mode.time = reds_get_mm_time();
> -    mode.mode = playback_channel->mode;
> -    spice_marshall_msg_playback_mode(channel->send_data.marshaller, &mode);
> -
> -    return snd_begin_send_message(channel);
> -}
> -
> -static void snd_playback_send(void* data)
> -{
> -    PlaybackChannel *playback_channel = (PlaybackChannel*)data;
> -    SndChannel *channel = (SndChannel*)playback_channel;
> -
> -    if (!playback_channel || !snd_send_data(data)) {
> -        return;
> -    }
> -
> -    while (channel->command) {
> -        if (channel->command & SND_PLAYBACK_MODE_MASK) {
> -            if (!playback_send_mode(playback_channel)) {
> -                return;
> -            }
> -            channel->command &= ~SND_PLAYBACK_MODE_MASK;
> -        }
> -        if (channel->command & SND_PLAYBACK_PCM_MASK) {
> -            spice_assert(!playback_channel->in_progress && playback_channel->pending_frame);
> -            playback_channel->in_progress = playback_channel->pending_frame;
> -            playback_channel->pending_frame = NULL;
> -            channel->command &= ~SND_PLAYBACK_PCM_MASK;
> -            if (!snd_playback_send_write(playback_channel)) {
> -                spice_printerr("snd_send_playback_write failed");
> -                return;
> -            }
> -        }
> -        if (channel->command & SND_PLAYBACK_CTRL_MASK) {
> -            if (!snd_playback_send_ctl(playback_channel)) {
> -                return;
> -            }
> -            channel->command &= ~SND_PLAYBACK_CTRL_MASK;
> -        }
> -        if (channel->command & SND_PLAYBACK_VOLUME_MASK) {
> -            if (!snd_playback_send_volume(playback_channel) ||
> -                !snd_playback_send_mute(playback_channel)) {
> -                return;
> -            }
> -            channel->command &= ~SND_PLAYBACK_VOLUME_MASK;
> -        }
> -        if (channel->command & SND_PLAYBACK_MIGRATE_MASK) {
> -            if (!snd_playback_send_migrate(playback_channel)) {
> -                return;
> -            }
> -            channel->command &= ~SND_PLAYBACK_MIGRATE_MASK;
> -        }
> -        if (channel->command & SND_PLAYBACK_LATENCY_MASK) {
> -            if (!snd_playback_send_latency(playback_channel)) {
> -                return;
> -            }
> -            channel->command &= ~SND_PLAYBACK_LATENCY_MASK;
> -        }
> -    }
> -}
> -
> -static void snd_record_send(void* data)
> -{
> -    RecordChannel *record_channel = (RecordChannel*)data;
> -    SndChannel *channel = (SndChannel*)record_channel;
> -
> -    if (!record_channel || !snd_send_data(data)) {
> -        return;
> -    }
> -
> -    while (channel->command) {
> -        if (channel->command & SND_RECORD_CTRL_MASK) {
> -            if (!snd_record_send_ctl(record_channel)) {
> -                return;
> -            }
> -            channel->command &= ~SND_RECORD_CTRL_MASK;
> -        }
> -        if (channel->command & SND_RECORD_VOLUME_MASK) {
> -            if (!snd_record_send_volume(record_channel) ||
> -                !snd_record_send_mute(record_channel)) {
> -                return;
> -            }
> -            channel->command &= ~SND_RECORD_VOLUME_MASK;
> -        }
> -        if (channel->command & SND_RECORD_MIGRATE_MASK) {
> -            if (!snd_record_send_migrate(record_channel)) {
> -                return;
> -            }
> -            channel->command &= ~SND_RECORD_MIGRATE_MASK;
> -        }
> -    }
> -}
> -
> -static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_id,
> -                                 RedClient *client,
> -                                 RedsStream *stream,
> -                                 int migrate,
> -                                 snd_channel_send_messages_proc send_messages,
> -                                 snd_channel_handle_message_proc handle_message,
> -                                 snd_channel_on_message_done_proc on_message_done,
> -                                 snd_channel_cleanup_channel_proc cleanup,
> -                                 uint32_t *common_caps, int num_common_caps,
> -                                 uint32_t *caps, int num_caps)
> -{
> -    SndChannel *channel;
> -    int delay_val;
> -    int flags;
> -#ifdef SO_PRIORITY
> -    int priority;
> -#endif
> -    int tos;
> -    MainChannelClient *mcc = red_client_get_main(client);
> -
> -    spice_assert(stream);
> -    if ((flags = fcntl(stream->socket, F_GETFL)) == -1) {
> -        spice_printerr("accept failed, %s", strerror(errno));
> -        goto error1;
> -    }
> -
> -#ifdef SO_PRIORITY
> -    priority = 6;
> -    if (setsockopt(stream->socket, SOL_SOCKET, SO_PRIORITY, (void*)&priority,
> -                   sizeof(priority)) == -1) {
> -        if (errno != ENOTSUP) {
> -            spice_printerr("setsockopt failed, %s", strerror(errno));
> -        }
> -    }
> -#endif
> -
> -    tos = IPTOS_LOWDELAY;
> -    if (setsockopt(stream->socket, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) == -1) {
> -        if (errno != ENOTSUP) {
> -            spice_printerr("setsockopt failed, %s", strerror(errno));
> -        }
> -    }
> -
> -    delay_val = main_channel_client_is_low_bandwidth(mcc) ? 0 : 1;
> -    if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) {
> -        if (errno != ENOTSUP) {
> -            spice_printerr("setsockopt failed, %s", strerror(errno));
> -        }
> -    }
> -
> -    if (fcntl(stream->socket, F_SETFL, flags | O_NONBLOCK) == -1) {
> -        spice_printerr("accept failed, %s", strerror(errno));
> -        goto error1;
> -    }
> -
> -    spice_assert(size >= sizeof(*channel));
> -    channel = spice_malloc0(size);
> -    channel->refs = 1;
> -    channel->parser = spice_get_client_channel_parser(channel_id, NULL);
> -    channel->stream = stream;
> -    channel->worker = worker;
> -    channel->receive_data.message_start = channel->receive_data.buf;
> -    channel->receive_data.now = channel->receive_data.buf;
> -    channel->receive_data.end = channel->receive_data.buf + sizeof(channel->receive_data.buf);
> -    channel->send_data.marshaller = spice_marshaller_new();
> -
> -    stream->watch = core->watch_add(stream->socket, SPICE_WATCH_EVENT_READ,
> -                                  snd_event, channel);
> -    if (stream->watch == NULL) {
> -        spice_printerr("watch_add failed, %s", strerror(errno));
> -        goto error2;
> -    }
> -
> -    channel->send_messages = send_messages;
> -    channel->handle_message = handle_message;
> -    channel->on_message_done = on_message_done;
> -    channel->cleanup = cleanup;
> -
> -    channel->channel_client = red_channel_client_create_dummy(sizeof(RedChannelClient),
> -                                                              worker->base_channel,
> -                                                              client,
> -                                                              num_common_caps, common_caps,
> -                                                              num_caps, caps);
> -    if (!channel->channel_client) {
> -        goto error2;
> -    }
> -    return channel;
> -
> -error2:
> -    free(channel);
> -
> -error1:
> -    reds_stream_free(stream);
> -    return NULL;
> -}
> -
> -static void snd_disconnect_channel_client(RedChannelClient *rcc)
> -{
> -    SndWorker *worker;
> -
> -    spice_assert(rcc->channel);
> -    spice_assert(rcc->channel->data);
> -    worker = (SndWorker *)rcc->channel->data;
> -
> -    spice_debug("channel-type=%d", rcc->channel->type);
> -    if (worker->connection) {
> -        spice_assert(worker->connection->channel_client == rcc);
> -        snd_disconnect_channel(worker->connection);
> -    }
> -}
> -
> -static void snd_set_command(SndChannel *channel, uint32_t command)
> -{
> -    if (!channel) {
> -        return;
> -    }
> -    channel->command |= command;
> -}
> -
> -SPICE_GNUC_VISIBLE void spice_server_playback_set_volume(SpicePlaybackInstance *sin,
> -                                                  uint8_t nchannels,
> -                                                  uint16_t *volume)
> -{
> -    SpiceVolumeState *st = &sin->st->volume;
> -    SndChannel *channel = sin->st->worker.connection;
> -    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> -
> -    st->volume_nchannels = nchannels;
> -    free(st->volume);
> -    st->volume = spice_memdup(volume, sizeof(uint16_t) * nchannels);
> -
> -    if (!channel || nchannels == 0)
> -        return;
> -
> -    snd_playback_send_volume(playback_channel);
> -}
> -
> -SPICE_GNUC_VISIBLE void spice_server_playback_set_mute(SpicePlaybackInstance *sin, uint8_t mute)
> -{
> -    SpiceVolumeState *st = &sin->st->volume;
> -    SndChannel *channel = sin->st->worker.connection;
> -    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> -
> -    st->mute = mute;
> -
> -    if (!channel)
> -        return;
> -
> -    snd_playback_send_mute(playback_channel);
> -}
> -
> -SPICE_GNUC_VISIBLE void spice_server_playback_start(SpicePlaybackInstance *sin)
> -{
> -    SndChannel *channel = sin->st->worker.connection;
> -    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> -
> -    sin->st->worker.active = 1;
> -    if (!channel)
> -        return;
> -    spice_assert(!playback_channel->base.active);
> -    reds_disable_mm_time();
> -    playback_channel->base.active = TRUE;
> -    if (!playback_channel->base.client_active) {
> -        snd_set_command(&playback_channel->base, SND_PLAYBACK_CTRL_MASK);
> -        snd_playback_send(&playback_channel->base);
> -    } else {
> -        playback_channel->base.command &= ~SND_PLAYBACK_CTRL_MASK;
> -    }
> -}
> -
> -SPICE_GNUC_VISIBLE void spice_server_playback_stop(SpicePlaybackInstance *sin)
> -{
> -    SndChannel *channel = sin->st->worker.connection;
> -    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> -
> -    sin->st->worker.active = 0;
> -    if (!channel)
> -        return;
> -    spice_assert(playback_channel->base.active);
> -    reds_enable_mm_time();
> -    playback_channel->base.active = FALSE;
> -    if (playback_channel->base.client_active) {
> -        snd_set_command(&playback_channel->base, SND_PLAYBACK_CTRL_MASK);
> -        snd_playback_send(&playback_channel->base);
> -    } else {
> -        playback_channel->base.command &= ~SND_PLAYBACK_CTRL_MASK;
> -        playback_channel->base.command &= ~SND_PLAYBACK_PCM_MASK;
> -
> -        if (playback_channel->pending_frame) {
> -            spice_assert(!playback_channel->in_progress);
> -            snd_playback_free_frame(playback_channel,
> -                                    playback_channel->pending_frame);
> -            playback_channel->pending_frame = NULL;
> -        }
> -    }
> -}
> -
> -SPICE_GNUC_VISIBLE void spice_server_playback_get_buffer(SpicePlaybackInstance *sin,
> -                                                         uint32_t **frame, uint32_t *num_samples)
> -{
> -    SndChannel *channel = sin->st->worker.connection;
> -    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> -
> -    if (!channel || !playback_channel->free_frames) {
> -        *frame = NULL;
> -        *num_samples = 0;
> -        return;
> -    }
> -    spice_assert(playback_channel->base.active);
> -    snd_channel_get(channel);
> -
> -    *frame = playback_channel->free_frames->samples;
> -    playback_channel->free_frames = playback_channel->free_frames->next;
> -    *num_samples = snd_codec_frame_size(playback_channel->codec);
> -}
> -
> -SPICE_GNUC_VISIBLE void spice_server_playback_put_samples(SpicePlaybackInstance *sin, uint32_t *samples)
> -{
> -    PlaybackChannel *playback_channel;
> -    AudioFrame *frame;
> -
> -    frame = SPICE_CONTAINEROF(samples, AudioFrame, samples);
> -    playback_channel = frame->channel;
> -    spice_assert(playback_channel);
> -    if (!snd_channel_put(&playback_channel->base) ||
> -        sin->st->worker.connection != &playback_channel->base) {
> -        /* lost last reference, channel has been destroyed previously */
> -        spice_info("audio samples belong to a disconnected channel");
> -        return;
> -    }
> -    spice_assert(playback_channel->base.active);
> -
> -    if (playback_channel->pending_frame) {
> -        snd_playback_free_frame(playback_channel, playback_channel->pending_frame);
> -    }
> -    frame->time = reds_get_mm_time();
> -    playback_channel->pending_frame = frame;
> -    snd_set_command(&playback_channel->base, SND_PLAYBACK_PCM_MASK);
> -    snd_playback_send(&playback_channel->base);
> -}
> -
> -void snd_set_playback_latency(RedClient *client, uint32_t latency)
> -{
> -    SndWorker *now = workers;
> -
> -    for (; now; now = now->next) {
> -        if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection &&
> -            now->connection->channel_client->client == client) {
> -
> -            if (red_channel_client_test_remote_cap(now->connection->channel_client,
> -                SPICE_PLAYBACK_CAP_LATENCY)) {
> -                PlaybackChannel* playback = (PlaybackChannel*)now->connection;
> -
> -                playback->latency = latency;
> -                snd_set_command(now->connection, SND_PLAYBACK_LATENCY_MASK);
> -                snd_playback_send(now->connection);
> -            } else {
> -                spice_debug("client doesn't not support SPICE_PLAYBACK_CAP_LATENCY");
> -            }
> -        }
> -    }
> -}
> -
> -static int snd_desired_audio_mode(int frequency, int client_can_celt, int client_can_opus)
> -{
> -    if (! playback_compression)
> -        return SPICE_AUDIO_DATA_MODE_RAW;
> -
> -    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
> -        return SPICE_AUDIO_DATA_MODE_OPUS;
> -
> -    if (client_can_celt && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, frequency))
> -        return SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
> -
> -    return SPICE_AUDIO_DATA_MODE_RAW;
> -}
> -
> -static void on_new_playback_channel(SndWorker *worker)
> -{
> -    PlaybackChannel *playback_channel =
> -        SPICE_CONTAINEROF(worker->connection, PlaybackChannel, base);
> -    SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker);
> -
> -    spice_assert(playback_channel);
> -
> -    snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_MODE_MASK);
> -    if (playback_channel->base.active) {
> -        snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_CTRL_MASK);
> -    }
> -    if (st->volume.volume_nchannels) {
> -        snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_VOLUME_MASK);
> -    }
> -    if (playback_channel->base.active) {
> -        reds_disable_mm_time();
> -    }
> -}
> -
> -static void snd_playback_cleanup(SndChannel *channel)
> -{
> -    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> -
> -    if (playback_channel->base.active) {
> -        reds_enable_mm_time();
> -    }
> -
> -    snd_codec_destroy(&playback_channel->codec);
> -}
> -
> -static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
> -                                  int migration, int num_common_caps, uint32_t *common_caps,
> -                                  int num_caps, uint32_t *caps)
> -{
> -    SndWorker *worker = channel->data;
> -    PlaybackChannel *playback_channel;
> -    SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker);
> -
> -    snd_disconnect_channel(worker->connection);
> -
> -    if (!(playback_channel = (PlaybackChannel *)__new_channel(worker,
> -                                                              sizeof(*playback_channel),
> -                                                              SPICE_CHANNEL_PLAYBACK,
> -                                                              client,
> -                                                              stream,
> -                                                              migration,
> -                                                              snd_playback_send,
> -                                                              snd_playback_handle_message,
> -                                                              snd_playback_on_message_done,
> -                                                              snd_playback_cleanup,
> -                                                              common_caps, num_common_caps,
> -                                                              caps, num_caps))) {
> -        return;
> -    }
> -    worker->connection = &playback_channel->base;
> -    snd_playback_free_frame(playback_channel, &playback_channel->frames[0]);
> -    snd_playback_free_frame(playback_channel, &playback_channel->frames[1]);
> -    snd_playback_free_frame(playback_channel, &playback_channel->frames[2]);
> -
> -    int client_can_celt = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
> -                                          SPICE_PLAYBACK_CAP_CELT_0_5_1);
> -    int client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
> -                                          SPICE_PLAYBACK_CAP_OPUS);
> -    int desired_mode = snd_desired_audio_mode(st->frequency, client_can_celt, client_can_opus);
> -    playback_channel->mode = SPICE_AUDIO_DATA_MODE_RAW;
> -    if (desired_mode != SPICE_AUDIO_DATA_MODE_RAW) {
> -        if (snd_codec_create(&playback_channel->codec, desired_mode, st->frequency, SND_CODEC_ENCODE) == SND_CODEC_OK) {
> -            playback_channel->mode = desired_mode;
> -        } else {
> -            spice_printerr("create encoder failed");
> -        }
> -    }
> -
> -    if (!red_client_during_migrate_at_target(client)) {
> -        on_new_playback_channel(worker);
> -    }
> -
> -    if (worker->active) {
> -        spice_server_playback_start(st->sin);
> -    }
> -    snd_playback_send(worker->connection);
> -}
> -
> -static void snd_record_migrate_channel_client(RedChannelClient *rcc)
> -{
> -    SndWorker *worker;
> -
> -    spice_debug(NULL);
> -    spice_assert(rcc->channel);
> -    spice_assert(rcc->channel->data);
> -    worker = (SndWorker *)rcc->channel->data;
> -
> -    if (worker->connection) {
> -        spice_assert(worker->connection->channel_client == rcc);
> -        snd_set_command(worker->connection, SND_RECORD_MIGRATE_MASK);
> -        snd_record_send(worker->connection);
> -    }
> -}
> -
> -SPICE_GNUC_VISIBLE void spice_server_record_set_volume(SpiceRecordInstance *sin,
> -                                                uint8_t nchannels,
> -                                                uint16_t *volume)
> -{
> -    SpiceVolumeState *st = &sin->st->volume;
> -    SndChannel *channel = sin->st->worker.connection;
> -    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> -
> -    st->volume_nchannels = nchannels;
> -    free(st->volume);
> -    st->volume = spice_memdup(volume, sizeof(uint16_t) * nchannels);
> -
> -    if (!channel || nchannels == 0)
> -        return;
> -
> -    snd_record_send_volume(record_channel);
> -}
> -
> -SPICE_GNUC_VISIBLE void spice_server_record_set_mute(SpiceRecordInstance *sin, uint8_t mute)
> -{
> -    SpiceVolumeState *st = &sin->st->volume;
> -    SndChannel *channel = sin->st->worker.connection;
> -    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> -
> -    st->mute = mute;
> -
> -    if (!channel)
> -        return;
> -
> -    snd_record_send_mute(record_channel);
> -}
> -
> -SPICE_GNUC_VISIBLE void spice_server_record_start(SpiceRecordInstance *sin)
> -{
> -    SndChannel *channel = sin->st->worker.connection;
> -    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> -
> -    sin->st->worker.active = 1;
> -    if (!channel)
> -        return;
> -    spice_assert(!record_channel->base.active);
> -    record_channel->base.active = TRUE;
> -    record_channel->read_pos = record_channel->write_pos = 0;   //todo: improve by
> -                                                                //stream generation
> -    if (!record_channel->base.client_active) {
> -        snd_set_command(&record_channel->base, SND_RECORD_CTRL_MASK);
> -        snd_record_send(&record_channel->base);
> -    } else {
> -        record_channel->base.command &= ~SND_RECORD_CTRL_MASK;
> -    }
> -}
> -
> -SPICE_GNUC_VISIBLE void spice_server_record_stop(SpiceRecordInstance *sin)
> -{
> -    SndChannel *channel = sin->st->worker.connection;
> -    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> -
> -    sin->st->worker.active = 0;
> -    if (!channel)
> -        return;
> -    spice_assert(record_channel->base.active);
> -    record_channel->base.active = FALSE;
> -    if (record_channel->base.client_active) {
> -        snd_set_command(&record_channel->base, SND_RECORD_CTRL_MASK);
> -        snd_record_send(&record_channel->base);
> -    } else {
> -        record_channel->base.command &= ~SND_RECORD_CTRL_MASK;
> -    }
> -}
> -
> -SPICE_GNUC_VISIBLE uint32_t spice_server_record_get_samples(SpiceRecordInstance *sin,
> -                                                            uint32_t *samples, uint32_t bufsize)
> -{
> -    SndChannel *channel = sin->st->worker.connection;
> -    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> -    uint32_t read_pos;
> -    uint32_t now;
> -    uint32_t len;
> -
> -    if (!channel)
> -        return 0;
> -    spice_assert(record_channel->base.active);
> -
> -    if (record_channel->write_pos < RECORD_SAMPLES_SIZE / 2) {
> -        return 0;
> -    }
> -
> -    len = MIN(record_channel->write_pos - record_channel->read_pos, bufsize);
> -
> -    if (len < bufsize) {
> -        SndWorker *worker = record_channel->base.worker;
> -        snd_receive(record_channel);
> -        if (!worker->connection) {
> -            return 0;
> -        }
> -        len = MIN(record_channel->write_pos - record_channel->read_pos, bufsize);
> -    }
> -
> -    read_pos = record_channel->read_pos % RECORD_SAMPLES_SIZE;
> -    record_channel->read_pos += len;
> -    now = MIN(len, RECORD_SAMPLES_SIZE - read_pos);
> -    memcpy(samples, &record_channel->samples[read_pos], now * 4);
> -    if (now < len) {
> -        memcpy(samples + now, record_channel->samples, (len - now) * 4);
> -    }
> -    return len;
> -}
> -
> -SPICE_GNUC_VISIBLE uint32_t spice_server_get_best_playback_rate(SpicePlaybackInstance *sin)
> -{
> -    int client_can_opus = TRUE;
> -    if (sin && sin->st->worker.connection) {
> -        SndChannel *channel = sin->st->worker.connection;
> -        PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> -        client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
> -                                          SPICE_PLAYBACK_CAP_OPUS);
> -    }
> -
> -    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
> -        return SND_CODEC_OPUS_PLAYBACK_FREQ;
> -
> -    return SND_CODEC_CELT_PLAYBACK_FREQ;
> -}
> -
> -SPICE_GNUC_VISIBLE void spice_server_set_playback_rate(SpicePlaybackInstance *sin, uint32_t frequency)
> -{
> -    RedChannel *channel = sin->st->worker.base_channel;
> -    sin->st->frequency = frequency;
> -    if (channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
> -        red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_OPUS);
> -}
> -
> -SPICE_GNUC_VISIBLE uint32_t spice_server_get_best_record_rate(SpiceRecordInstance *sin)
> -{
> -    int client_can_opus = TRUE;
> -    if (sin && sin->st->worker.connection) {
> -        SndChannel *channel = sin->st->worker.connection;
> -        RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> -        client_can_opus = red_channel_client_test_remote_cap(record_channel->base.channel_client,
> -                                          SPICE_RECORD_CAP_OPUS);
> -    }
> -
> -    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
> -        return SND_CODEC_OPUS_PLAYBACK_FREQ;
> -
> -    return SND_CODEC_CELT_PLAYBACK_FREQ;
> -}
> -
> -SPICE_GNUC_VISIBLE void spice_server_set_record_rate(SpiceRecordInstance *sin, uint32_t frequency)
> -{
> -    RedChannel *channel = sin->st->worker.base_channel;
> -    sin->st->frequency = frequency;
> -    if (channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
> -        red_channel_set_cap(channel, SPICE_RECORD_CAP_OPUS);
> -}
> -
> -static void on_new_record_channel(SndWorker *worker)
> -{
> -    RecordChannel *record_channel = (RecordChannel *)worker->connection;
> -    SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker);
> -
> -    spice_assert(record_channel);
> -
> -    if (st->volume.volume_nchannels) {
> -        snd_set_command((SndChannel *)record_channel, SND_RECORD_VOLUME_MASK);
> -    }
> -    if (record_channel->base.active) {
> -        snd_set_command((SndChannel *)record_channel, SND_RECORD_CTRL_MASK);
> -    }
> -}
> -
> -static void snd_record_cleanup(SndChannel *channel)
> -{
> -    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> -    snd_codec_destroy(&record_channel->codec);
> -}
> -
> -static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
> -                                int migration, int num_common_caps, uint32_t *common_caps,
> -                                int num_caps, uint32_t *caps)
> -{
> -    SndWorker *worker = channel->data;
> -    RecordChannel *record_channel;
> -    SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker);
> -
> -    snd_disconnect_channel(worker->connection);
> -
> -    if (!(record_channel = (RecordChannel *)__new_channel(worker,
> -                                                          sizeof(*record_channel),
> -                                                          SPICE_CHANNEL_RECORD,
> -                                                          client,
> -                                                          stream,
> -                                                          migration,
> -                                                          snd_record_send,
> -                                                          snd_record_handle_message,
> -                                                          snd_record_on_message_done,
> -                                                          snd_record_cleanup,
> -                                                          common_caps, num_common_caps,
> -                                                          caps, num_caps))) {
> -        return;
> -    }
> -
> -    record_channel->mode = SPICE_AUDIO_DATA_MODE_RAW;
> -
> -    worker->connection = &record_channel->base;
> -
> -    on_new_record_channel(worker);
> -    if (worker->active) {
> -        spice_server_record_start(st->sin);
> -    }
> -    snd_record_send(worker->connection);
> -}
> -
> -static void snd_playback_migrate_channel_client(RedChannelClient *rcc)
> -{
> -    SndWorker *worker;
> -
> -    spice_assert(rcc->channel);
> -    spice_assert(rcc->channel->data);
> -    worker = (SndWorker *)rcc->channel->data;
> -    spice_debug(NULL);
> -
> -    if (worker->connection) {
> -        spice_assert(worker->connection->channel_client == rcc);
> -        snd_set_command(worker->connection, SND_PLAYBACK_MIGRATE_MASK);
> -        snd_playback_send(worker->connection);
> -    }
> -}
> -
> -static void add_worker(SndWorker *worker)
> -{
> -    worker->next = workers;
> -    workers = worker;
> -}
> -
> -static void remove_worker(SndWorker *worker)
> -{
> -    SndWorker **now = &workers;
> -    while (*now) {
> -        if (*now == worker) {
> -            *now = worker->next;
> -            return;
> -        }
> -        now = &(*now)->next;
> -    }
> -    spice_printerr("not found");
> -}
> -
> -void snd_attach_playback(SpicePlaybackInstance *sin)
> -{
> -    SndWorker *playback_worker;
> -    RedChannel *channel;
> -    ClientCbs client_cbs = { NULL, };
> -
> -    sin->st = spice_new0(SpicePlaybackState, 1);
> -    sin->st->sin = sin;
> -    playback_worker = &sin->st->worker;
> -    sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
> -
> -    // TODO: Make RedChannel base of worker? instead of assigning it to channel->data
> -    channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_PLAYBACK, 0);
> -
> -    channel->data = playback_worker;
> -    client_cbs.connect = snd_set_playback_peer;
> -    client_cbs.disconnect = snd_disconnect_channel_client;
> -    client_cbs.migrate = snd_playback_migrate_channel_client;
> -    red_channel_register_client_cbs(channel, &client_cbs);
> -    red_channel_set_data(channel, playback_worker);
> -
> -    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
> -        red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_CELT_0_5_1);
> -
> -    red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_VOLUME);
> -
> -    playback_worker->base_channel = channel;
> -    add_worker(playback_worker);
> -    reds_register_channel(playback_worker->base_channel);
> -}
> -
> -void snd_attach_record(SpiceRecordInstance *sin)
> -{
> -    SndWorker *record_worker;
> -    RedChannel *channel;
> -    ClientCbs client_cbs = { NULL, };
> -
> -    sin->st = spice_new0(SpiceRecordState, 1);
> -    sin->st->sin = sin;
> -    record_worker = &sin->st->worker;
> -    sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
> -
> -    // TODO: Make RedChannel base of worker? instead of assigning it to channel->data
> -    channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_RECORD, 0);
> -
> -    channel->data = record_worker;
> -    client_cbs.connect = snd_set_record_peer;
> -    client_cbs.disconnect = snd_disconnect_channel_client;
> -    client_cbs.migrate = snd_record_migrate_channel_client;
> -    red_channel_register_client_cbs(channel, &client_cbs);
> -    red_channel_set_data(channel, record_worker);
> -    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
> -        red_channel_set_cap(channel, SPICE_RECORD_CAP_CELT_0_5_1);
> -    red_channel_set_cap(channel, SPICE_RECORD_CAP_VOLUME);
> -
> -    record_worker->base_channel = channel;
> -    add_worker(record_worker);
> -    reds_register_channel(record_worker->base_channel);
> -}
> -
> -static void snd_detach_common(SndWorker *worker)
> -{
> -    if (!worker) {
> -        return;
> -    }
> -    remove_worker(worker);
> -    snd_disconnect_channel(worker->connection);
> -    reds_unregister_channel(worker->base_channel);
> -    red_channel_destroy(worker->base_channel);
> -}
> -
> -static void spice_playback_state_free(SpicePlaybackState *st)
> -{
> -    free(st->volume.volume);
> -    free(st);
> -}
> -
> -void snd_detach_playback(SpicePlaybackInstance *sin)
> -{
> -    snd_detach_common(&sin->st->worker);
> -    spice_playback_state_free(sin->st);
> -}
> -
> -static void spice_record_state_free(SpiceRecordState *st)
> -{
> -    free(st->volume.volume);
> -    free(st);
> -}
> -
> -void snd_detach_record(SpiceRecordInstance *sin)
> -{
> -    snd_detach_common(&sin->st->worker);
> -    spice_record_state_free(sin->st);
> -}
> -
> -void snd_set_playback_compression(int on)
> -{
> -    SndWorker *now = workers;
> -
> -    playback_compression = !!on;
> -
> -    for (; now; now = now->next) {
> -        if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) {
> -            PlaybackChannel* playback = (PlaybackChannel*)now->connection;
> -            SpicePlaybackState *st = SPICE_CONTAINEROF(now, SpicePlaybackState, worker);
> -            int client_can_celt = red_channel_client_test_remote_cap(playback->base.channel_client,
> -                                    SPICE_PLAYBACK_CAP_CELT_0_5_1);
> -            int client_can_opus = red_channel_client_test_remote_cap(playback->base.channel_client,
> -                                    SPICE_PLAYBACK_CAP_OPUS);
> -            int desired_mode = snd_desired_audio_mode(st->frequency, client_can_opus, client_can_celt);
> -            if (playback->mode != desired_mode) {
> -                playback->mode = desired_mode;
> -                snd_set_command(now->connection, SND_PLAYBACK_MODE_MASK);
> -            }
> -        }
> -    }
> -}
> diff --git a/server/snd_worker.h b/server/snd_worker.h
> deleted file mode 100644
> index 7cc4db5..0000000
> --- a/server/snd_worker.h
> +++ /dev/null
> @@ -1,33 +0,0 @@
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -
> -#ifndef _H_SND_WORKER
> -#define _H_SND_WORKER
> -
> -#include "spice.h"
> -
> -void snd_attach_playback(SpicePlaybackInstance *sin);
> -void snd_detach_playback(SpicePlaybackInstance *sin);
> -
> -void snd_attach_record(SpiceRecordInstance *sin);
> -void snd_detach_record(SpiceRecordInstance *sin);
> -
> -void snd_set_playback_compression(int on);
> -
> -void snd_set_playback_latency(RedClient *client, uint32_t latency);
> -
> -#endif
> diff --git a/server/sound.c b/server/sound.c
> new file mode 100644
> index 0000000..a91d56e
> --- /dev/null
> +++ b/server/sound.c
> @@ -0,0 +1,1625 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <fcntl.h>
> +#include <errno.h>
> +#include <limits.h>
> +#include <sys/socket.h>
> +#include <netinet/ip.h>
> +#include <netinet/tcp.h>
> +
> +#include "common/marshaller.h"
> +#include "common/generated_server_marshallers.h"
> +
> +#include "spice.h"
> +#include "red_common.h"
> +#include "main-channel.h"
> +#include "reds.h"
> +#include "red_dispatcher.h"
> +#include "sound.h"
> +#include "common/snd_codec.h"
> +#include "demarshallers.h"
> +
> +#ifndef IOV_MAX
> +#define IOV_MAX 1024
> +#endif
> +
> +#define SND_RECEIVE_BUF_SIZE     (16 * 1024 * 2)
> +#define RECORD_SAMPLES_SIZE (SND_RECEIVE_BUF_SIZE >> 2)
> +
> +enum PlaybackCommand {
> +    SND_PLAYBACK_MIGRATE,
> +    SND_PLAYBACK_MODE,
> +    SND_PLAYBACK_CTRL,
> +    SND_PLAYBACK_PCM,
> +    SND_PLAYBACK_VOLUME,
> +    SND_PLAYBACK_LATENCY,
> +};
> +
> +enum RecordCommand {
> +    SND_RECORD_MIGRATE,
> +    SND_RECORD_CTRL,
> +    SND_RECORD_VOLUME,
> +};
> +
> +#define SND_PLAYBACK_MIGRATE_MASK (1 << SND_PLAYBACK_MIGRATE)
> +#define SND_PLAYBACK_MODE_MASK (1 << SND_PLAYBACK_MODE)
> +#define SND_PLAYBACK_CTRL_MASK (1 << SND_PLAYBACK_CTRL)
> +#define SND_PLAYBACK_PCM_MASK (1 << SND_PLAYBACK_PCM)
> +#define SND_PLAYBACK_VOLUME_MASK (1 << SND_PLAYBACK_VOLUME)
> +#define SND_PLAYBACK_LATENCY_MASK ( 1 << SND_PLAYBACK_LATENCY)
> +
> +#define SND_RECORD_MIGRATE_MASK (1 << SND_RECORD_MIGRATE)
> +#define SND_RECORD_CTRL_MASK (1 << SND_RECORD_CTRL)
> +#define SND_RECORD_VOLUME_MASK (1 << SND_RECORD_VOLUME)
> +
> +typedef struct SndChannel SndChannel;
> +typedef void (*snd_channel_send_messages_proc)(void *in_channel);
> +typedef int (*snd_channel_handle_message_proc)(SndChannel *channel, size_t size, uint32_t type, void *message);
> +typedef void (*snd_channel_on_message_done_proc)(SndChannel *channel);
> +typedef void (*snd_channel_cleanup_channel_proc)(SndChannel *channel);
> +
> +typedef struct SndWorker SndWorker;
> +
> +struct SndChannel {
> +    RedsStream *stream;
> +    SndWorker *worker;
> +    spice_parse_channel_func_t parser;
> +    int refs;
> +
> +    RedChannelClient *channel_client;
> +
> +    int active;
> +    int client_active;
> +    int blocked;
> +
> +    uint32_t command;
> +    uint32_t ack_generation;
> +    uint32_t client_ack_generation;
> +    uint32_t out_messages;
> +    uint32_t ack_messages;
> +
> +    struct {
> +        uint64_t serial;
> +        SpiceMarshaller *marshaller;
> +        uint32_t size;
> +        uint32_t pos;
> +    } send_data;
> +
> +    struct {
> +        uint8_t buf[SND_RECEIVE_BUF_SIZE];
> +        uint8_t *message_start;
> +        uint8_t *now;
> +        uint8_t *end;
> +    } receive_data;
> +
> +    snd_channel_send_messages_proc send_messages;
> +    snd_channel_handle_message_proc handle_message;
> +    snd_channel_on_message_done_proc on_message_done;
> +    snd_channel_cleanup_channel_proc cleanup;
> +};
> +
> +typedef struct PlaybackChannel PlaybackChannel;
> +
> +typedef struct AudioFrame AudioFrame;
> +struct AudioFrame {
> +    uint32_t time;
> +    uint32_t samples[SND_CODEC_MAX_FRAME_SIZE];
> +    PlaybackChannel *channel;
> +    AudioFrame *next;
> +};
> +
> +struct PlaybackChannel {
> +    SndChannel base;
> +    AudioFrame frames[3];
> +    AudioFrame *free_frames;
> +    AudioFrame *in_progress;
> +    AudioFrame *pending_frame;
> +    uint32_t mode;
> +    uint32_t latency;
> +    SndCodec codec;
> +    uint8_t  encode_buf[SND_CODEC_MAX_COMPRESSED_BYTES];
> +};
> +
> +struct SndWorker {
> +    RedChannel *base_channel;
> +    SndChannel *connection;
> +    SndWorker *next;
> +    int active;
> +};
> +
> +typedef struct SpiceVolumeState {
> +    uint8_t volume_nchannels;
> +    uint16_t *volume;
> +    int mute;
> +} SpiceVolumeState;
> +
> +struct SpicePlaybackState {
> +    struct SndWorker worker;
> +    SpicePlaybackInstance *sin;
> +    SpiceVolumeState volume;
> +    uint32_t frequency;
> +};
> +
> +struct SpiceRecordState {
> +    struct SndWorker worker;
> +    SpiceRecordInstance *sin;
> +    SpiceVolumeState volume;
> +    uint32_t frequency;
> +};
> +
> +typedef struct RecordChannel {
> +    SndChannel base;
> +    uint32_t samples[RECORD_SAMPLES_SIZE];
> +    uint32_t write_pos;
> +    uint32_t read_pos;
> +    uint32_t mode;
> +    uint32_t mode_time;
> +    uint32_t start_time;
> +    SndCodec codec;
> +    uint8_t  decode_buf[SND_CODEC_MAX_FRAME_BYTES];
> +} RecordChannel;
> +
> +static SndWorker *workers;
> +static uint32_t playback_compression = TRUE;
> +
> +static void snd_receive(void* data);
> +
> +static SndChannel *snd_channel_get(SndChannel *channel)
> +{
> +    channel->refs++;
> +    return channel;
> +}
> +
> +static SndChannel *snd_channel_put(SndChannel *channel)
> +{
> +    if (!--channel->refs) {
> +        spice_printerr("SndChannel=%p freed", channel);
> +        free(channel);
> +        return NULL;
> +    }
> +    return channel;
> +}
> +
> +static void snd_disconnect_channel(SndChannel *channel)
> +{
> +    SndWorker *worker;
> +
> +    if (!channel || !channel->stream) {
> +        spice_debug("not connected");
> +        return;
> +    }
> +    spice_debug("SndChannel=%p rcc=%p type=%d",
> +                 channel, channel->channel_client, channel->channel_client->channel->type);
> +    worker = channel->worker;
> +    channel->cleanup(channel);
> +    red_channel_client_disconnect(worker->connection->channel_client);
> +    worker->connection->channel_client = NULL;
> +    core->watch_remove(channel->stream->watch);
> +    channel->stream->watch = NULL;
> +    reds_stream_free(channel->stream);
> +    channel->stream = NULL;
> +    spice_marshaller_destroy(channel->send_data.marshaller);
> +    snd_channel_put(channel);
> +    worker->connection = NULL;
> +}
> +
> +static void snd_playback_free_frame(PlaybackChannel *playback_channel, AudioFrame *frame)
> +{
> +    frame->channel = playback_channel;
> +    frame->next = playback_channel->free_frames;
> +    playback_channel->free_frames = frame;
> +}
> +
> +static void snd_playback_on_message_done(SndChannel *channel)
> +{
> +    PlaybackChannel *playback_channel = (PlaybackChannel *)channel;
> +    if (playback_channel->in_progress) {
> +        snd_playback_free_frame(playback_channel, playback_channel->in_progress);
> +        playback_channel->in_progress = NULL;
> +        if (playback_channel->pending_frame) {
> +            channel->command |= SND_PLAYBACK_PCM_MASK;
> +        }
> +    }
> +}
> +
> +static void snd_record_on_message_done(SndChannel *channel)
> +{
> +}
> +
> +static int snd_send_data(SndChannel *channel)
> +{
> +    uint32_t n;
> +
> +    if (!channel) {
> +        return FALSE;
> +    }
> +
> +    if (!(n = channel->send_data.size - channel->send_data.pos)) {
> +        return TRUE;
> +    }
> +
> +    for (;;) {
> +        struct iovec vec[IOV_MAX];
> +        int vec_size;
> +
> +        if (!n) {
> +            channel->on_message_done(channel);
> +
> +            if (channel->blocked) {
> +                channel->blocked = FALSE;
> +                core->watch_update_mask(channel->stream->watch, SPICE_WATCH_EVENT_READ);
> +            }
> +            break;
> +        }
> +
> +        vec_size = spice_marshaller_fill_iovec(channel->send_data.marshaller,
> +                                               vec, IOV_MAX, channel->send_data.pos);
> +        n = reds_stream_writev(channel->stream, vec, vec_size);
> +        if (n == -1) {
> +            switch (errno) {
> +            case EAGAIN:
> +                channel->blocked = TRUE;
> +                core->watch_update_mask(channel->stream->watch, SPICE_WATCH_EVENT_READ |
> +                                        SPICE_WATCH_EVENT_WRITE);
> +                return FALSE;
> +            case EINTR:
> +                break;
> +            case EPIPE:
> +                snd_disconnect_channel(channel);
> +                return FALSE;
> +            default:
> +                spice_printerr("%s", strerror(errno));
> +                snd_disconnect_channel(channel);
> +                return FALSE;
> +            }
> +        } else {
> +            channel->send_data.pos += n;
> +        }
> +        n = channel->send_data.size - channel->send_data.pos;
> +    }
> +    return TRUE;
> +}
> +
> +static int snd_record_handle_write(RecordChannel *record_channel, size_t size, void *message)
> +{
> +    SpiceMsgcRecordPacket *packet;
> +    uint32_t write_pos;
> +    uint32_t* data;
> +    uint32_t len;
> +    uint32_t now;
> +
> +    if (!record_channel) {
> +        return FALSE;
> +    }
> +
> +    packet = (SpiceMsgcRecordPacket *)message;
> +
> +    if (record_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
> +        data = (uint32_t *)packet->data;
> +        size = packet->data_size >> 2;
> +        size = MIN(size, RECORD_SAMPLES_SIZE);
> +     } else {
> +        int decode_size;
> +        decode_size = sizeof(record_channel->decode_buf);
> +        if (snd_codec_decode(record_channel->codec, packet->data, packet->data_size,
> +                    record_channel->decode_buf, &decode_size) != SND_CODEC_OK)
> +            return FALSE;
> +        data = (uint32_t *) record_channel->decode_buf;
> +        size = decode_size >> 2;
> +    }
> +
> +    write_pos = record_channel->write_pos % RECORD_SAMPLES_SIZE;
> +    record_channel->write_pos += size;
> +    len = RECORD_SAMPLES_SIZE - write_pos;
> +    now = MIN(len, size);
> +    size -= now;
> +    memcpy(record_channel->samples + write_pos, data, now << 2);
> +
> +    if (size) {
> +        memcpy(record_channel->samples, data + now, size << 2);
> +    }
> +
> +    if (record_channel->write_pos - record_channel->read_pos > RECORD_SAMPLES_SIZE) {
> +        record_channel->read_pos = record_channel->write_pos - RECORD_SAMPLES_SIZE;
> +    }
> +    return TRUE;
> +}
> +
> +static int snd_playback_handle_message(SndChannel *channel, size_t size, uint32_t type, void *message)
> +{
> +    if (!channel) {
> +        return FALSE;
> +    }
> +
> +    switch (type) {
> +    case SPICE_MSGC_DISCONNECTING:
> +        break;
> +    default:
> +        spice_printerr("invalid message type %u", type);
> +        return FALSE;
> +    }
> +    return TRUE;
> +}
> +
> +static int snd_record_handle_message(SndChannel *channel, size_t size, uint32_t type, void *message)
> +{
> +    RecordChannel *record_channel = (RecordChannel *)channel;
> +
> +    if (!channel) {
> +        return FALSE;
> +    }
> +    switch (type) {
> +    case SPICE_MSGC_RECORD_DATA:
> +        return snd_record_handle_write((RecordChannel *)channel, size, message);
> +    case SPICE_MSGC_RECORD_MODE: {
> +        SpiceMsgcRecordMode *mode = (SpiceMsgcRecordMode *)message;
> +        SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
> +        record_channel->mode_time = mode->time;
> +        if (mode->mode != SPICE_AUDIO_DATA_MODE_RAW) {
> +            if (snd_codec_is_capable(mode->mode, st->frequency)) {
> +                if (snd_codec_create(&record_channel->codec, mode->mode, st->frequency, SND_CODEC_DECODE) == SND_CODEC_OK) {
> +                    record_channel->mode = mode->mode;
> +                } else {
> +                    spice_printerr("create decoder failed");
> +                    return FALSE;
> +                }
> +            }
> +            else {
> +                spice_printerr("unsupported mode %d", record_channel->mode);
> +                return FALSE;
> +            }
> +        }
> +        else
> +            record_channel->mode = mode->mode;
> +        break;
> +    }
> +
> +    case SPICE_MSGC_RECORD_START_MARK: {
> +        SpiceMsgcRecordStartMark *mark = (SpiceMsgcRecordStartMark *)message;
> +        record_channel->start_time = mark->time;
> +        break;
> +    }
> +    case SPICE_MSGC_DISCONNECTING:
> +        break;
> +    default:
> +        spice_printerr("invalid message type %u", type);
> +        return FALSE;
> +    }
> +    return TRUE;
> +}
> +
> +static void snd_receive(void* data)
> +{
> +    SndChannel *channel = (SndChannel*)data;
> +    SpiceDataHeaderOpaque *header;
> +
> +    if (!channel) {
> +        return;
> +    }
> +
> +    header = &channel->channel_client->incoming.header;
> +
> +    for (;;) {
> +        ssize_t n;
> +        n = channel->receive_data.end - channel->receive_data.now;
> +        spice_warn_if(n <= 0);
> +        n = reds_stream_read(channel->stream, channel->receive_data.now, n);
> +        if (n <= 0) {
> +            if (n == 0) {
> +                snd_disconnect_channel(channel);
> +                return;
> +            }
> +            spice_assert(n == -1);
> +            switch (errno) {
> +            case EAGAIN:
> +                return;
> +            case EINTR:
> +                break;
> +            case EPIPE:
> +                snd_disconnect_channel(channel);
> +                return;
> +            default:
> +                spice_printerr("%s", strerror(errno));
> +                snd_disconnect_channel(channel);
> +                return;
> +            }
> +        } else {
> +            channel->receive_data.now += n;
> +            for (;;) {
> +                uint8_t *msg_start = channel->receive_data.message_start;
> +                uint8_t *data = msg_start + header->header_size;
> +                size_t parsed_size;
> +                uint8_t *parsed;
> +                message_destructor_t parsed_free;
> +
> +                header->data = msg_start;
> +                n = channel->receive_data.now - msg_start;
> +
> +                if (n < header->header_size ||
> +                    n < header->header_size + header->get_msg_size(header)) {
> +                    break;
> +                }
> +                parsed = channel->parser((void *)data, data + header->get_msg_size(header),
> +                                         header->get_msg_type(header),
> +                                         SPICE_VERSION_MINOR, &parsed_size, &parsed_free);
> +                if (parsed == NULL) {
> +                    spice_printerr("failed to parse message type %d", header->get_msg_type(header));
> +                    snd_disconnect_channel(channel);
> +                    return;
> +                }
> +                if (!channel->handle_message(channel, parsed_size,
> +                                             header->get_msg_type(header), parsed)) {
> +                    free(parsed);
> +                    snd_disconnect_channel(channel);
> +                    return;
> +                }
> +                parsed_free(parsed);
> +                channel->receive_data.message_start = msg_start + header->header_size +
> +                                                     header->get_msg_size(header);
> +            }
> +            if (channel->receive_data.now == channel->receive_data.message_start) {
> +                channel->receive_data.now = channel->receive_data.buf;
> +                channel->receive_data.message_start = channel->receive_data.buf;
> +            } else if (channel->receive_data.now == channel->receive_data.end) {
> +                memcpy(channel->receive_data.buf, channel->receive_data.message_start, n);
> +                channel->receive_data.now = channel->receive_data.buf + n;
> +                channel->receive_data.message_start = channel->receive_data.buf;
> +            }
> +        }
> +    }
> +}
> +
> +static void snd_event(int fd, int event, void *data)
> +{
> +    SndChannel *channel = data;
> +
> +    if (event & SPICE_WATCH_EVENT_READ) {
> +        snd_receive(channel);
> +    }
> +    if (event & SPICE_WATCH_EVENT_WRITE) {
> +        channel->send_messages(channel);
> +    }
> +}
> +
> +static inline int snd_reset_send_data(SndChannel *channel, uint16_t verb)
> +{
> +    SpiceDataHeaderOpaque *header;
> +
> +    if (!channel) {
> +        return FALSE;
> +    }
> +
> +    header = &channel->channel_client->send_data.header;
> +    spice_marshaller_reset(channel->send_data.marshaller);
> +    header->data = spice_marshaller_reserve_space(channel->send_data.marshaller,
> +                                                  header->header_size);
> +    spice_marshaller_set_base(channel->send_data.marshaller,
> +                              header->header_size);
> +    channel->send_data.pos = 0;
> +    header->set_msg_size(header, 0);
> +    header->set_msg_type(header, verb);
> +    channel->send_data.serial++;
> +    if (!channel->channel_client->is_mini_header) {
> +        header->set_msg_serial(header, channel->send_data.serial);
> +        header->set_msg_sub_list(header, 0);
> +    }
> +
> +    return TRUE;
> +}
> +
> +static int snd_begin_send_message(SndChannel *channel)
> +{
> +    SpiceDataHeaderOpaque *header = &channel->channel_client->send_data.header;
> +
> +    spice_marshaller_flush(channel->send_data.marshaller);
> +    channel->send_data.size = spice_marshaller_get_total_size(channel->send_data.marshaller);
> +    header->set_msg_size(header, channel->send_data.size - header->header_size);
> +    return snd_send_data(channel);
> +}
> +
> +static int snd_channel_send_migrate(SndChannel *channel)
> +{
> +    SpiceMsgMigrate migrate;
> +
> +    if (!snd_reset_send_data(channel, SPICE_MSG_MIGRATE)) {
> +        return FALSE;
> +    }
> +    spice_debug(NULL);
> +    migrate.flags = 0;
> +    spice_marshall_msg_migrate(channel->send_data.marshaller, &migrate);
> +
> +    return snd_begin_send_message(channel);
> +}
> +
> +static int snd_playback_send_migrate(PlaybackChannel *channel)
> +{
> +    return snd_channel_send_migrate(&channel->base);
> +}
> +
> +static int snd_send_volume(SndChannel *channel, SpiceVolumeState *st, int msg)
> +{
> +    SpiceMsgAudioVolume *vol;
> +    uint8_t c;
> +
> +    vol = alloca(sizeof (SpiceMsgAudioVolume) +
> +                 st->volume_nchannels * sizeof (uint16_t));
> +    if (!snd_reset_send_data(channel, msg)) {
> +        return FALSE;
> +    }
> +    vol->nchannels = st->volume_nchannels;
> +    for (c = 0; c < st->volume_nchannels; ++c) {
> +        vol->volume[c] = st->volume[c];
> +    }
> +    spice_marshall_SpiceMsgAudioVolume(channel->send_data.marshaller, vol);
> +
> +    return snd_begin_send_message(channel);
> +}
> +
> +static int snd_playback_send_volume(PlaybackChannel *playback_channel)
> +{
> +    SndChannel *channel = &playback_channel->base;
> +    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
> +
> +    if (!red_channel_client_test_remote_cap(channel->channel_client,
> +                                            SPICE_PLAYBACK_CAP_VOLUME)) {
> +        return TRUE;
> +    }
> +
> +    return snd_send_volume(channel, &st->volume, SPICE_MSG_PLAYBACK_VOLUME);
> +}
> +
> +static int snd_send_mute(SndChannel *channel, SpiceVolumeState *st, int msg)
> +{
> +    SpiceMsgAudioMute mute;
> +
> +    if (!snd_reset_send_data(channel, msg)) {
> +        return FALSE;
> +    }
> +    mute.mute = st->mute;
> +    spice_marshall_SpiceMsgAudioMute(channel->send_data.marshaller, &mute);
> +
> +    return snd_begin_send_message(channel);
> +}
> +
> +static int snd_playback_send_mute(PlaybackChannel *playback_channel)
> +{
> +    SndChannel *channel = &playback_channel->base;
> +    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
> +
> +    if (!red_channel_client_test_remote_cap(channel->channel_client,
> +                                            SPICE_PLAYBACK_CAP_VOLUME)) {
> +        return TRUE;
> +    }
> +
> +    return snd_send_mute(channel, &st->volume, SPICE_MSG_PLAYBACK_MUTE);
> +}
> +
> +static int snd_playback_send_latency(PlaybackChannel *playback_channel)
> +{
> +    SndChannel *channel = &playback_channel->base;
> +    SpiceMsgPlaybackLatency latency_msg;
> +
> +    spice_debug("latency %u", playback_channel->latency);
> +    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_LATENCY)) {
> +        return FALSE;
> +    }
> +    latency_msg.latency_ms = playback_channel->latency;
> +    spice_marshall_msg_playback_latency(channel->send_data.marshaller, &latency_msg);
> +
> +    return snd_begin_send_message(channel);
> +}
> +static int snd_playback_send_start(PlaybackChannel *playback_channel)
> +{
> +    SndChannel *channel = (SndChannel *)playback_channel;
> +    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
> +    SpiceMsgPlaybackStart start;
> +
> +    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_START)) {
> +        return FALSE;
> +    }
> +
> +    start.channels = SPICE_INTERFACE_PLAYBACK_CHAN;
> +    start.frequency = st->frequency;
> +    spice_assert(SPICE_INTERFACE_PLAYBACK_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
> +    start.format = SPICE_AUDIO_FMT_S16;
> +    start.time = reds_get_mm_time();
> +    spice_marshall_msg_playback_start(channel->send_data.marshaller, &start);
> +
> +    return snd_begin_send_message(channel);
> +}
> +
> +static int snd_playback_send_stop(PlaybackChannel *playback_channel)
> +{
> +    SndChannel *channel = (SndChannel *)playback_channel;
> +
> +    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_STOP)) {
> +        return FALSE;
> +    }
> +
> +    return snd_begin_send_message(channel);
> +}
> +
> +static int snd_playback_send_ctl(PlaybackChannel *playback_channel)
> +{
> +    SndChannel *channel = (SndChannel *)playback_channel;
> +
> +    if ((channel->client_active = channel->active)) {
> +        return snd_playback_send_start(playback_channel);
> +    } else {
> +        return snd_playback_send_stop(playback_channel);
> +    }
> +}
> +
> +static int snd_record_send_start(RecordChannel *record_channel)
> +{
> +    SndChannel *channel = (SndChannel *)record_channel;
> +    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
> +    SpiceMsgRecordStart start;
> +
> +    if (!snd_reset_send_data(channel, SPICE_MSG_RECORD_START)) {
> +        return FALSE;
> +    }
> +
> +    start.channels = SPICE_INTERFACE_RECORD_CHAN;
> +    start.frequency = st->frequency;
> +    spice_assert(SPICE_INTERFACE_RECORD_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
> +    start.format = SPICE_AUDIO_FMT_S16;
> +    spice_marshall_msg_record_start(channel->send_data.marshaller, &start);
> +
> +    return snd_begin_send_message(channel);
> +}
> +
> +static int snd_record_send_stop(RecordChannel *record_channel)
> +{
> +    SndChannel *channel = (SndChannel *)record_channel;
> +
> +    if (!snd_reset_send_data(channel, SPICE_MSG_RECORD_STOP)) {
> +        return FALSE;
> +    }
> +
> +    return snd_begin_send_message(channel);
> +}
> +
> +static int snd_record_send_ctl(RecordChannel *record_channel)
> +{
> +    SndChannel *channel = (SndChannel *)record_channel;
> +
> +    if ((channel->client_active = channel->active)) {
> +        return snd_record_send_start(record_channel);
> +    } else {
> +        return snd_record_send_stop(record_channel);
> +    }
> +}
> +
> +static int snd_record_send_volume(RecordChannel *record_channel)
> +{
> +    SndChannel *channel = &record_channel->base;
> +    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
> +
> +    if (!red_channel_client_test_remote_cap(channel->channel_client,
> +                                            SPICE_RECORD_CAP_VOLUME)) {
> +        return TRUE;
> +    }
> +
> +    return snd_send_volume(channel, &st->volume, SPICE_MSG_RECORD_VOLUME);
> +}
> +
> +static int snd_record_send_mute(RecordChannel *record_channel)
> +{
> +    SndChannel *channel = &record_channel->base;
> +    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
> +
> +    if (!red_channel_client_test_remote_cap(channel->channel_client,
> +                                            SPICE_RECORD_CAP_VOLUME)) {
> +        return TRUE;
> +    }
> +
> +    return snd_send_mute(channel, &st->volume, SPICE_MSG_RECORD_MUTE);
> +}
> +
> +static int snd_record_send_migrate(RecordChannel *record_channel)
> +{
> +    /* No need for migration data: if recording has started before migration,
> +     * the client receives RECORD_STOP from the src before the migration completion
> +     * notification (when the vm is stopped).
> +     * Afterwards, when the vm starts on the dest, the client receives RECORD_START. */
> +    return snd_channel_send_migrate(&record_channel->base);
> +}
> +
> +static int snd_playback_send_write(PlaybackChannel *playback_channel)
> +{
> +    SndChannel *channel = (SndChannel *)playback_channel;
> +    AudioFrame *frame;
> +    SpiceMsgPlaybackPacket msg;
> +
> +    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_DATA)) {
> +        return FALSE;
> +    }
> +
> +    frame = playback_channel->in_progress;
> +    msg.time = frame->time;
> +
> +    spice_marshall_msg_playback_data(channel->send_data.marshaller, &msg);
> +
> +    if (playback_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
> +        spice_marshaller_add_ref(channel->send_data.marshaller,
> +                                 (uint8_t *)frame->samples,
> +                                 snd_codec_frame_size(playback_channel->codec) * sizeof(frame->samples[0]));
> +    }
> +    else {
> +        int n = sizeof(playback_channel->encode_buf);
> +        if (snd_codec_encode(playback_channel->codec, (uint8_t *) frame->samples,
> +                                    snd_codec_frame_size(playback_channel->codec) * sizeof(frame->samples[0]),
> +                                    playback_channel->encode_buf, &n) != SND_CODEC_OK) {
> +            spice_printerr("encode failed");
> +            snd_disconnect_channel(channel);
> +            return FALSE;
> +        }
> +        spice_marshaller_add_ref(channel->send_data.marshaller, playback_channel->encode_buf, n);
> +    }
> +
> +    return snd_begin_send_message(channel);
> +}
> +
> +static int playback_send_mode(PlaybackChannel *playback_channel)
> +{
> +    SndChannel *channel = (SndChannel *)playback_channel;
> +    SpiceMsgPlaybackMode mode;
> +
> +    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_MODE)) {
> +        return FALSE;
> +    }
> +    mode.time = reds_get_mm_time();
> +    mode.mode = playback_channel->mode;
> +    spice_marshall_msg_playback_mode(channel->send_data.marshaller, &mode);
> +
> +    return snd_begin_send_message(channel);
> +}
> +
> +static void snd_playback_send(void* data)
> +{
> +    PlaybackChannel *playback_channel = (PlaybackChannel*)data;
> +    SndChannel *channel = (SndChannel*)playback_channel;
> +
> +    if (!playback_channel || !snd_send_data(data)) {
> +        return;
> +    }
> +
> +    while (channel->command) {
> +        if (channel->command & SND_PLAYBACK_MODE_MASK) {
> +            if (!playback_send_mode(playback_channel)) {
> +                return;
> +            }
> +            channel->command &= ~SND_PLAYBACK_MODE_MASK;
> +        }
> +        if (channel->command & SND_PLAYBACK_PCM_MASK) {
> +            spice_assert(!playback_channel->in_progress && playback_channel->pending_frame);
> +            playback_channel->in_progress = playback_channel->pending_frame;
> +            playback_channel->pending_frame = NULL;
> +            channel->command &= ~SND_PLAYBACK_PCM_MASK;
> +            if (!snd_playback_send_write(playback_channel)) {
> +                spice_printerr("snd_send_playback_write failed");
> +                return;
> +            }
> +        }
> +        if (channel->command & SND_PLAYBACK_CTRL_MASK) {
> +            if (!snd_playback_send_ctl(playback_channel)) {
> +                return;
> +            }
> +            channel->command &= ~SND_PLAYBACK_CTRL_MASK;
> +        }
> +        if (channel->command & SND_PLAYBACK_VOLUME_MASK) {
> +            if (!snd_playback_send_volume(playback_channel) ||
> +                !snd_playback_send_mute(playback_channel)) {
> +                return;
> +            }
> +            channel->command &= ~SND_PLAYBACK_VOLUME_MASK;
> +        }
> +        if (channel->command & SND_PLAYBACK_MIGRATE_MASK) {
> +            if (!snd_playback_send_migrate(playback_channel)) {
> +                return;
> +            }
> +            channel->command &= ~SND_PLAYBACK_MIGRATE_MASK;
> +        }
> +        if (channel->command & SND_PLAYBACK_LATENCY_MASK) {
> +            if (!snd_playback_send_latency(playback_channel)) {
> +                return;
> +            }
> +            channel->command &= ~SND_PLAYBACK_LATENCY_MASK;
> +        }
> +    }
> +}
> +
> +static void snd_record_send(void* data)
> +{
> +    RecordChannel *record_channel = (RecordChannel*)data;
> +    SndChannel *channel = (SndChannel*)record_channel;
> +
> +    if (!record_channel || !snd_send_data(data)) {
> +        return;
> +    }
> +
> +    while (channel->command) {
> +        if (channel->command & SND_RECORD_CTRL_MASK) {
> +            if (!snd_record_send_ctl(record_channel)) {
> +                return;
> +            }
> +            channel->command &= ~SND_RECORD_CTRL_MASK;
> +        }
> +        if (channel->command & SND_RECORD_VOLUME_MASK) {
> +            if (!snd_record_send_volume(record_channel) ||
> +                !snd_record_send_mute(record_channel)) {
> +                return;
> +            }
> +            channel->command &= ~SND_RECORD_VOLUME_MASK;
> +        }
> +        if (channel->command & SND_RECORD_MIGRATE_MASK) {
> +            if (!snd_record_send_migrate(record_channel)) {
> +                return;
> +            }
> +            channel->command &= ~SND_RECORD_MIGRATE_MASK;
> +        }
> +    }
> +}
> +
> +static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_id,
> +                                 RedClient *client,
> +                                 RedsStream *stream,
> +                                 int migrate,
> +                                 snd_channel_send_messages_proc send_messages,
> +                                 snd_channel_handle_message_proc handle_message,
> +                                 snd_channel_on_message_done_proc on_message_done,
> +                                 snd_channel_cleanup_channel_proc cleanup,
> +                                 uint32_t *common_caps, int num_common_caps,
> +                                 uint32_t *caps, int num_caps)
> +{
> +    SndChannel *channel;
> +    int delay_val;
> +    int flags;
> +#ifdef SO_PRIORITY
> +    int priority;
> +#endif
> +    int tos;
> +    MainChannelClient *mcc = red_client_get_main(client);
> +
> +    spice_assert(stream);
> +    if ((flags = fcntl(stream->socket, F_GETFL)) == -1) {
> +        spice_printerr("accept failed, %s", strerror(errno));
> +        goto error1;
> +    }
> +
> +#ifdef SO_PRIORITY
> +    priority = 6;
> +    if (setsockopt(stream->socket, SOL_SOCKET, SO_PRIORITY, (void*)&priority,
> +                   sizeof(priority)) == -1) {
> +        if (errno != ENOTSUP) {
> +            spice_printerr("setsockopt failed, %s", strerror(errno));
> +        }
> +    }
> +#endif
> +
> +    tos = IPTOS_LOWDELAY;
> +    if (setsockopt(stream->socket, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) == -1) {
> +        if (errno != ENOTSUP) {
> +            spice_printerr("setsockopt failed, %s", strerror(errno));
> +        }
> +    }
> +
> +    delay_val = main_channel_client_is_low_bandwidth(mcc) ? 0 : 1;
> +    if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) {
> +        if (errno != ENOTSUP) {
> +            spice_printerr("setsockopt failed, %s", strerror(errno));
> +        }
> +    }
> +
> +    if (fcntl(stream->socket, F_SETFL, flags | O_NONBLOCK) == -1) {
> +        spice_printerr("accept failed, %s", strerror(errno));
> +        goto error1;
> +    }
> +
> +    spice_assert(size >= sizeof(*channel));
> +    channel = spice_malloc0(size);
> +    channel->refs = 1;
> +    channel->parser = spice_get_client_channel_parser(channel_id, NULL);
> +    channel->stream = stream;
> +    channel->worker = worker;
> +    channel->receive_data.message_start = channel->receive_data.buf;
> +    channel->receive_data.now = channel->receive_data.buf;
> +    channel->receive_data.end = channel->receive_data.buf + sizeof(channel->receive_data.buf);
> +    channel->send_data.marshaller = spice_marshaller_new();
> +
> +    stream->watch = core->watch_add(stream->socket, SPICE_WATCH_EVENT_READ,
> +                                  snd_event, channel);
> +    if (stream->watch == NULL) {
> +        spice_printerr("watch_add failed, %s", strerror(errno));
> +        goto error2;
> +    }
> +
> +    channel->send_messages = send_messages;
> +    channel->handle_message = handle_message;
> +    channel->on_message_done = on_message_done;
> +    channel->cleanup = cleanup;
> +
> +    channel->channel_client = red_channel_client_create_dummy(sizeof(RedChannelClient),
> +                                                              worker->base_channel,
> +                                                              client,
> +                                                              num_common_caps, common_caps,
> +                                                              num_caps, caps);
> +    if (!channel->channel_client) {
> +        goto error2;
> +    }
> +    return channel;
> +
> +error2:
> +    free(channel);
> +
> +error1:
> +    reds_stream_free(stream);
> +    return NULL;
> +}
> +
> +static void snd_disconnect_channel_client(RedChannelClient *rcc)
> +{
> +    SndWorker *worker;
> +
> +    spice_assert(rcc->channel);
> +    spice_assert(rcc->channel->data);
> +    worker = (SndWorker *)rcc->channel->data;
> +
> +    spice_debug("channel-type=%d", rcc->channel->type);
> +    if (worker->connection) {
> +        spice_assert(worker->connection->channel_client == rcc);
> +        snd_disconnect_channel(worker->connection);
> +    }
> +}
> +
> +static void snd_set_command(SndChannel *channel, uint32_t command)
> +{
> +    if (!channel) {
> +        return;
> +    }
> +    channel->command |= command;
> +}
> +
> +SPICE_GNUC_VISIBLE void spice_server_playback_set_volume(SpicePlaybackInstance *sin,
> +                                                  uint8_t nchannels,
> +                                                  uint16_t *volume)
> +{
> +    SpiceVolumeState *st = &sin->st->volume;
> +    SndChannel *channel = sin->st->worker.connection;
> +    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> +
> +    st->volume_nchannels = nchannels;
> +    free(st->volume);
> +    st->volume = spice_memdup(volume, sizeof(uint16_t) * nchannels);
> +
> +    if (!channel || nchannels == 0)
> +        return;
> +
> +    snd_playback_send_volume(playback_channel);
> +}
> +
> +SPICE_GNUC_VISIBLE void spice_server_playback_set_mute(SpicePlaybackInstance *sin, uint8_t mute)
> +{
> +    SpiceVolumeState *st = &sin->st->volume;
> +    SndChannel *channel = sin->st->worker.connection;
> +    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> +
> +    st->mute = mute;
> +
> +    if (!channel)
> +        return;
> +
> +    snd_playback_send_mute(playback_channel);
> +}
> +
> +SPICE_GNUC_VISIBLE void spice_server_playback_start(SpicePlaybackInstance *sin)
> +{
> +    SndChannel *channel = sin->st->worker.connection;
> +    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> +
> +    sin->st->worker.active = 1;
> +    if (!channel)
> +        return;
> +    spice_assert(!playback_channel->base.active);
> +    reds_disable_mm_time();
> +    playback_channel->base.active = TRUE;
> +    if (!playback_channel->base.client_active) {
> +        snd_set_command(&playback_channel->base, SND_PLAYBACK_CTRL_MASK);
> +        snd_playback_send(&playback_channel->base);
> +    } else {
> +        playback_channel->base.command &= ~SND_PLAYBACK_CTRL_MASK;
> +    }
> +}
> +
> +SPICE_GNUC_VISIBLE void spice_server_playback_stop(SpicePlaybackInstance *sin)
> +{
> +    SndChannel *channel = sin->st->worker.connection;
> +    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> +
> +    sin->st->worker.active = 0;
> +    if (!channel)
> +        return;
> +    spice_assert(playback_channel->base.active);
> +    reds_enable_mm_time();
> +    playback_channel->base.active = FALSE;
> +    if (playback_channel->base.client_active) {
> +        snd_set_command(&playback_channel->base, SND_PLAYBACK_CTRL_MASK);
> +        snd_playback_send(&playback_channel->base);
> +    } else {
> +        playback_channel->base.command &= ~SND_PLAYBACK_CTRL_MASK;
> +        playback_channel->base.command &= ~SND_PLAYBACK_PCM_MASK;
> +
> +        if (playback_channel->pending_frame) {
> +            spice_assert(!playback_channel->in_progress);
> +            snd_playback_free_frame(playback_channel,
> +                                    playback_channel->pending_frame);
> +            playback_channel->pending_frame = NULL;
> +        }
> +    }
> +}
> +
> +SPICE_GNUC_VISIBLE void spice_server_playback_get_buffer(SpicePlaybackInstance *sin,
> +                                                         uint32_t **frame, uint32_t *num_samples)
> +{
> +    SndChannel *channel = sin->st->worker.connection;
> +    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> +
> +    if (!channel || !playback_channel->free_frames) {
> +        *frame = NULL;
> +        *num_samples = 0;
> +        return;
> +    }
> +    spice_assert(playback_channel->base.active);
> +    snd_channel_get(channel);
> +
> +    *frame = playback_channel->free_frames->samples;
> +    playback_channel->free_frames = playback_channel->free_frames->next;
> +    *num_samples = snd_codec_frame_size(playback_channel->codec);
> +}
> +
> +SPICE_GNUC_VISIBLE void spice_server_playback_put_samples(SpicePlaybackInstance *sin, uint32_t *samples)
> +{
> +    PlaybackChannel *playback_channel;
> +    AudioFrame *frame;
> +
> +    frame = SPICE_CONTAINEROF(samples, AudioFrame, samples);
> +    playback_channel = frame->channel;
> +    spice_assert(playback_channel);
> +    if (!snd_channel_put(&playback_channel->base) ||
> +        sin->st->worker.connection != &playback_channel->base) {
> +        /* lost last reference, channel has been destroyed previously */
> +        spice_info("audio samples belong to a disconnected channel");
> +        return;
> +    }
> +    spice_assert(playback_channel->base.active);
> +
> +    if (playback_channel->pending_frame) {
> +        snd_playback_free_frame(playback_channel, playback_channel->pending_frame);
> +    }
> +    frame->time = reds_get_mm_time();
> +    playback_channel->pending_frame = frame;
> +    snd_set_command(&playback_channel->base, SND_PLAYBACK_PCM_MASK);
> +    snd_playback_send(&playback_channel->base);
> +}
> +
> +void snd_set_playback_latency(RedClient *client, uint32_t latency)
> +{
> +    SndWorker *now = workers;
> +
> +    for (; now; now = now->next) {
> +        if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection &&
> +            now->connection->channel_client->client == client) {
> +
> +            if (red_channel_client_test_remote_cap(now->connection->channel_client,
> +                SPICE_PLAYBACK_CAP_LATENCY)) {
> +                PlaybackChannel* playback = (PlaybackChannel*)now->connection;
> +
> +                playback->latency = latency;
> +                snd_set_command(now->connection, SND_PLAYBACK_LATENCY_MASK);
> +                snd_playback_send(now->connection);
> +            } else {
> +                spice_debug("client doesn't not support SPICE_PLAYBACK_CAP_LATENCY");
> +            }
> +        }
> +    }
> +}
> +
> +static int snd_desired_audio_mode(int frequency, int client_can_celt, int client_can_opus)
> +{
> +    if (! playback_compression)
> +        return SPICE_AUDIO_DATA_MODE_RAW;
> +
> +    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
> +        return SPICE_AUDIO_DATA_MODE_OPUS;
> +
> +    if (client_can_celt && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, frequency))
> +        return SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
> +
> +    return SPICE_AUDIO_DATA_MODE_RAW;
> +}
> +
> +static void on_new_playback_channel(SndWorker *worker)
> +{
> +    PlaybackChannel *playback_channel =
> +        SPICE_CONTAINEROF(worker->connection, PlaybackChannel, base);
> +    SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker);
> +
> +    spice_assert(playback_channel);
> +
> +    snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_MODE_MASK);
> +    if (playback_channel->base.active) {
> +        snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_CTRL_MASK);
> +    }
> +    if (st->volume.volume_nchannels) {
> +        snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_VOLUME_MASK);
> +    }
> +    if (playback_channel->base.active) {
> +        reds_disable_mm_time();
> +    }
> +}
> +
> +static void snd_playback_cleanup(SndChannel *channel)
> +{
> +    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> +
> +    if (playback_channel->base.active) {
> +        reds_enable_mm_time();
> +    }
> +
> +    snd_codec_destroy(&playback_channel->codec);
> +}
> +
> +static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
> +                                  int migration, int num_common_caps, uint32_t *common_caps,
> +                                  int num_caps, uint32_t *caps)
> +{
> +    SndWorker *worker = channel->data;
> +    PlaybackChannel *playback_channel;
> +    SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker);
> +
> +    snd_disconnect_channel(worker->connection);
> +
> +    if (!(playback_channel = (PlaybackChannel *)__new_channel(worker,
> +                                                              sizeof(*playback_channel),
> +                                                              SPICE_CHANNEL_PLAYBACK,
> +                                                              client,
> +                                                              stream,
> +                                                              migration,
> +                                                              snd_playback_send,
> +                                                              snd_playback_handle_message,
> +                                                              snd_playback_on_message_done,
> +                                                              snd_playback_cleanup,
> +                                                              common_caps, num_common_caps,
> +                                                              caps, num_caps))) {
> +        return;
> +    }
> +    worker->connection = &playback_channel->base;
> +    snd_playback_free_frame(playback_channel, &playback_channel->frames[0]);
> +    snd_playback_free_frame(playback_channel, &playback_channel->frames[1]);
> +    snd_playback_free_frame(playback_channel, &playback_channel->frames[2]);
> +
> +    int client_can_celt = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
> +                                          SPICE_PLAYBACK_CAP_CELT_0_5_1);
> +    int client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
> +                                          SPICE_PLAYBACK_CAP_OPUS);
> +    int desired_mode = snd_desired_audio_mode(st->frequency, client_can_celt, client_can_opus);
> +    playback_channel->mode = SPICE_AUDIO_DATA_MODE_RAW;
> +    if (desired_mode != SPICE_AUDIO_DATA_MODE_RAW) {
> +        if (snd_codec_create(&playback_channel->codec, desired_mode, st->frequency, SND_CODEC_ENCODE) == SND_CODEC_OK) {
> +            playback_channel->mode = desired_mode;
> +        } else {
> +            spice_printerr("create encoder failed");
> +        }
> +    }
> +
> +    if (!red_client_during_migrate_at_target(client)) {
> +        on_new_playback_channel(worker);
> +    }
> +
> +    if (worker->active) {
> +        spice_server_playback_start(st->sin);
> +    }
> +    snd_playback_send(worker->connection);
> +}
> +
> +static void snd_record_migrate_channel_client(RedChannelClient *rcc)
> +{
> +    SndWorker *worker;
> +
> +    spice_debug(NULL);
> +    spice_assert(rcc->channel);
> +    spice_assert(rcc->channel->data);
> +    worker = (SndWorker *)rcc->channel->data;
> +
> +    if (worker->connection) {
> +        spice_assert(worker->connection->channel_client == rcc);
> +        snd_set_command(worker->connection, SND_RECORD_MIGRATE_MASK);
> +        snd_record_send(worker->connection);
> +    }
> +}
> +
> +SPICE_GNUC_VISIBLE void spice_server_record_set_volume(SpiceRecordInstance *sin,
> +                                                uint8_t nchannels,
> +                                                uint16_t *volume)
> +{
> +    SpiceVolumeState *st = &sin->st->volume;
> +    SndChannel *channel = sin->st->worker.connection;
> +    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> +
> +    st->volume_nchannels = nchannels;
> +    free(st->volume);
> +    st->volume = spice_memdup(volume, sizeof(uint16_t) * nchannels);
> +
> +    if (!channel || nchannels == 0)
> +        return;
> +
> +    snd_record_send_volume(record_channel);
> +}
> +
> +SPICE_GNUC_VISIBLE void spice_server_record_set_mute(SpiceRecordInstance *sin, uint8_t mute)
> +{
> +    SpiceVolumeState *st = &sin->st->volume;
> +    SndChannel *channel = sin->st->worker.connection;
> +    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> +
> +    st->mute = mute;
> +
> +    if (!channel)
> +        return;
> +
> +    snd_record_send_mute(record_channel);
> +}
> +
> +SPICE_GNUC_VISIBLE void spice_server_record_start(SpiceRecordInstance *sin)
> +{
> +    SndChannel *channel = sin->st->worker.connection;
> +    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> +
> +    sin->st->worker.active = 1;
> +    if (!channel)
> +        return;
> +    spice_assert(!record_channel->base.active);
> +    record_channel->base.active = TRUE;
> +    record_channel->read_pos = record_channel->write_pos = 0;   //todo: improve by
> +                                                                //stream generation
> +    if (!record_channel->base.client_active) {
> +        snd_set_command(&record_channel->base, SND_RECORD_CTRL_MASK);
> +        snd_record_send(&record_channel->base);
> +    } else {
> +        record_channel->base.command &= ~SND_RECORD_CTRL_MASK;
> +    }
> +}
> +
> +SPICE_GNUC_VISIBLE void spice_server_record_stop(SpiceRecordInstance *sin)
> +{
> +    SndChannel *channel = sin->st->worker.connection;
> +    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> +
> +    sin->st->worker.active = 0;
> +    if (!channel)
> +        return;
> +    spice_assert(record_channel->base.active);
> +    record_channel->base.active = FALSE;
> +    if (record_channel->base.client_active) {
> +        snd_set_command(&record_channel->base, SND_RECORD_CTRL_MASK);
> +        snd_record_send(&record_channel->base);
> +    } else {
> +        record_channel->base.command &= ~SND_RECORD_CTRL_MASK;
> +    }
> +}
> +
> +SPICE_GNUC_VISIBLE uint32_t spice_server_record_get_samples(SpiceRecordInstance *sin,
> +                                                            uint32_t *samples, uint32_t bufsize)
> +{
> +    SndChannel *channel = sin->st->worker.connection;
> +    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> +    uint32_t read_pos;
> +    uint32_t now;
> +    uint32_t len;
> +
> +    if (!channel)
> +        return 0;
> +    spice_assert(record_channel->base.active);
> +
> +    if (record_channel->write_pos < RECORD_SAMPLES_SIZE / 2) {
> +        return 0;
> +    }
> +
> +    len = MIN(record_channel->write_pos - record_channel->read_pos, bufsize);
> +
> +    if (len < bufsize) {
> +        SndWorker *worker = record_channel->base.worker;
> +        snd_receive(record_channel);
> +        if (!worker->connection) {
> +            return 0;
> +        }
> +        len = MIN(record_channel->write_pos - record_channel->read_pos, bufsize);
> +    }
> +
> +    read_pos = record_channel->read_pos % RECORD_SAMPLES_SIZE;
> +    record_channel->read_pos += len;
> +    now = MIN(len, RECORD_SAMPLES_SIZE - read_pos);
> +    memcpy(samples, &record_channel->samples[read_pos], now * 4);
> +    if (now < len) {
> +        memcpy(samples + now, record_channel->samples, (len - now) * 4);
> +    }
> +    return len;
> +}
> +
> +SPICE_GNUC_VISIBLE uint32_t spice_server_get_best_playback_rate(SpicePlaybackInstance *sin)
> +{
> +    int client_can_opus = TRUE;
> +    if (sin && sin->st->worker.connection) {
> +        SndChannel *channel = sin->st->worker.connection;
> +        PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
> +        client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
> +                                          SPICE_PLAYBACK_CAP_OPUS);
> +    }
> +
> +    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
> +        return SND_CODEC_OPUS_PLAYBACK_FREQ;
> +
> +    return SND_CODEC_CELT_PLAYBACK_FREQ;
> +}
> +
> +SPICE_GNUC_VISIBLE void spice_server_set_playback_rate(SpicePlaybackInstance *sin, uint32_t frequency)
> +{
> +    RedChannel *channel = sin->st->worker.base_channel;
> +    sin->st->frequency = frequency;
> +    if (channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
> +        red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_OPUS);
> +}
> +
> +SPICE_GNUC_VISIBLE uint32_t spice_server_get_best_record_rate(SpiceRecordInstance *sin)
> +{
> +    int client_can_opus = TRUE;
> +    if (sin && sin->st->worker.connection) {
> +        SndChannel *channel = sin->st->worker.connection;
> +        RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> +        client_can_opus = red_channel_client_test_remote_cap(record_channel->base.channel_client,
> +                                          SPICE_RECORD_CAP_OPUS);
> +    }
> +
> +    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
> +        return SND_CODEC_OPUS_PLAYBACK_FREQ;
> +
> +    return SND_CODEC_CELT_PLAYBACK_FREQ;
> +}
> +
> +SPICE_GNUC_VISIBLE void spice_server_set_record_rate(SpiceRecordInstance *sin, uint32_t frequency)
> +{
> +    RedChannel *channel = sin->st->worker.base_channel;
> +    sin->st->frequency = frequency;
> +    if (channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
> +        red_channel_set_cap(channel, SPICE_RECORD_CAP_OPUS);
> +}
> +
> +static void on_new_record_channel(SndWorker *worker)
> +{
> +    RecordChannel *record_channel = (RecordChannel *)worker->connection;
> +    SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker);
> +
> +    spice_assert(record_channel);
> +
> +    if (st->volume.volume_nchannels) {
> +        snd_set_command((SndChannel *)record_channel, SND_RECORD_VOLUME_MASK);
> +    }
> +    if (record_channel->base.active) {
> +        snd_set_command((SndChannel *)record_channel, SND_RECORD_CTRL_MASK);
> +    }
> +}
> +
> +static void snd_record_cleanup(SndChannel *channel)
> +{
> +    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
> +    snd_codec_destroy(&record_channel->codec);
> +}
> +
> +static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
> +                                int migration, int num_common_caps, uint32_t *common_caps,
> +                                int num_caps, uint32_t *caps)
> +{
> +    SndWorker *worker = channel->data;
> +    RecordChannel *record_channel;
> +    SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker);
> +
> +    snd_disconnect_channel(worker->connection);
> +
> +    if (!(record_channel = (RecordChannel *)__new_channel(worker,
> +                                                          sizeof(*record_channel),
> +                                                          SPICE_CHANNEL_RECORD,
> +                                                          client,
> +                                                          stream,
> +                                                          migration,
> +                                                          snd_record_send,
> +                                                          snd_record_handle_message,
> +                                                          snd_record_on_message_done,
> +                                                          snd_record_cleanup,
> +                                                          common_caps, num_common_caps,
> +                                                          caps, num_caps))) {
> +        return;
> +    }
> +
> +    record_channel->mode = SPICE_AUDIO_DATA_MODE_RAW;
> +
> +    worker->connection = &record_channel->base;
> +
> +    on_new_record_channel(worker);
> +    if (worker->active) {
> +        spice_server_record_start(st->sin);
> +    }
> +    snd_record_send(worker->connection);
> +}
> +
> +static void snd_playback_migrate_channel_client(RedChannelClient *rcc)
> +{
> +    SndWorker *worker;
> +
> +    spice_assert(rcc->channel);
> +    spice_assert(rcc->channel->data);
> +    worker = (SndWorker *)rcc->channel->data;
> +    spice_debug(NULL);
> +
> +    if (worker->connection) {
> +        spice_assert(worker->connection->channel_client == rcc);
> +        snd_set_command(worker->connection, SND_PLAYBACK_MIGRATE_MASK);
> +        snd_playback_send(worker->connection);
> +    }
> +}
> +
> +static void add_worker(SndWorker *worker)
> +{
> +    worker->next = workers;
> +    workers = worker;
> +}
> +
> +static void remove_worker(SndWorker *worker)
> +{
> +    SndWorker **now = &workers;
> +    while (*now) {
> +        if (*now == worker) {
> +            *now = worker->next;
> +            return;
> +        }
> +        now = &(*now)->next;
> +    }
> +    spice_printerr("not found");
> +}
> +
> +void snd_attach_playback(SpicePlaybackInstance *sin)
> +{
> +    SndWorker *playback_worker;
> +    RedChannel *channel;
> +    ClientCbs client_cbs = { NULL, };
> +
> +    sin->st = spice_new0(SpicePlaybackState, 1);
> +    sin->st->sin = sin;
> +    playback_worker = &sin->st->worker;
> +    sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
> +
> +    // TODO: Make RedChannel base of worker? instead of assigning it to channel->data
> +    channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_PLAYBACK, 0);
> +
> +    channel->data = playback_worker;
> +    client_cbs.connect = snd_set_playback_peer;
> +    client_cbs.disconnect = snd_disconnect_channel_client;
> +    client_cbs.migrate = snd_playback_migrate_channel_client;
> +    red_channel_register_client_cbs(channel, &client_cbs);
> +    red_channel_set_data(channel, playback_worker);
> +
> +    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
> +        red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_CELT_0_5_1);
> +
> +    red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_VOLUME);
> +
> +    playback_worker->base_channel = channel;
> +    add_worker(playback_worker);
> +    reds_register_channel(playback_worker->base_channel);
> +}
> +
> +void snd_attach_record(SpiceRecordInstance *sin)
> +{
> +    SndWorker *record_worker;
> +    RedChannel *channel;
> +    ClientCbs client_cbs = { NULL, };
> +
> +    sin->st = spice_new0(SpiceRecordState, 1);
> +    sin->st->sin = sin;
> +    record_worker = &sin->st->worker;
> +    sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
> +
> +    // TODO: Make RedChannel base of worker? instead of assigning it to channel->data
> +    channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_RECORD, 0);
> +
> +    channel->data = record_worker;
> +    client_cbs.connect = snd_set_record_peer;
> +    client_cbs.disconnect = snd_disconnect_channel_client;
> +    client_cbs.migrate = snd_record_migrate_channel_client;
> +    red_channel_register_client_cbs(channel, &client_cbs);
> +    red_channel_set_data(channel, record_worker);
> +    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
> +        red_channel_set_cap(channel, SPICE_RECORD_CAP_CELT_0_5_1);
> +    red_channel_set_cap(channel, SPICE_RECORD_CAP_VOLUME);
> +
> +    record_worker->base_channel = channel;
> +    add_worker(record_worker);
> +    reds_register_channel(record_worker->base_channel);
> +}
> +
> +static void snd_detach_common(SndWorker *worker)
> +{
> +    if (!worker) {
> +        return;
> +    }
> +    remove_worker(worker);
> +    snd_disconnect_channel(worker->connection);
> +    reds_unregister_channel(worker->base_channel);
> +    red_channel_destroy(worker->base_channel);
> +}
> +
> +static void spice_playback_state_free(SpicePlaybackState *st)
> +{
> +    free(st->volume.volume);
> +    free(st);
> +}
> +
> +void snd_detach_playback(SpicePlaybackInstance *sin)
> +{
> +    snd_detach_common(&sin->st->worker);
> +    spice_playback_state_free(sin->st);
> +}
> +
> +static void spice_record_state_free(SpiceRecordState *st)
> +{
> +    free(st->volume.volume);
> +    free(st);
> +}
> +
> +void snd_detach_record(SpiceRecordInstance *sin)
> +{
> +    snd_detach_common(&sin->st->worker);
> +    spice_record_state_free(sin->st);
> +}
> +
> +void snd_set_playback_compression(int on)
> +{
> +    SndWorker *now = workers;
> +
> +    playback_compression = !!on;
> +
> +    for (; now; now = now->next) {
> +        if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) {
> +            PlaybackChannel* playback = (PlaybackChannel*)now->connection;
> +            SpicePlaybackState *st = SPICE_CONTAINEROF(now, SpicePlaybackState, worker);
> +            int client_can_celt = red_channel_client_test_remote_cap(playback->base.channel_client,
> +                                    SPICE_PLAYBACK_CAP_CELT_0_5_1);
> +            int client_can_opus = red_channel_client_test_remote_cap(playback->base.channel_client,
> +                                    SPICE_PLAYBACK_CAP_OPUS);
> +            int desired_mode = snd_desired_audio_mode(st->frequency, client_can_opus, client_can_celt);
> +            if (playback->mode != desired_mode) {
> +                playback->mode = desired_mode;
> +                snd_set_command(now->connection, SND_PLAYBACK_MODE_MASK);
> +            }
> +        }
> +    }
> +}
> diff --git a/server/sound.h b/server/sound.h
> new file mode 100644
> index 0000000..97f8410
> --- /dev/null
> +++ b/server/sound.h
> @@ -0,0 +1,33 @@
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +
> +#ifndef SOUND_H_
> +#define SOUND_H_
> +
> +#include "spice.h"
> +
> +void snd_attach_playback(SpicePlaybackInstance *sin);
> +void snd_detach_playback(SpicePlaybackInstance *sin);
> +
> +void snd_attach_record(SpiceRecordInstance *sin);
> +void snd_detach_record(SpiceRecordInstance *sin);
> +
> +void snd_set_playback_compression(int on);
> +
> +void snd_set_playback_latency(RedClient *client, uint32_t latency);
> +
> +#endif
> diff --git a/server/spice_image_cache.c b/server/spice_image_cache.c
> deleted file mode 100644
> index 1c5de24..0000000
> --- a/server/spice_image_cache.c
> +++ /dev/null
> @@ -1,214 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009-2015 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifdef HAVE_CONFIG_H
> -#include <config.h>
> -#endif
> -#include "spice_image_cache.h"
> -#include "red_parse_qxl.h"
> -#include "display-channel.h"
> -
> -static ImageCacheItem *image_cache_find(ImageCache *cache, uint64_t id)
> -{
> -    ImageCacheItem *item = cache->hash_table[id % IMAGE_CACHE_HASH_SIZE];
> -
> -    while (item) {
> -        if (item->id == id) {
> -            return item;
> -        }
> -        item = item->next;
> -    }
> -    return NULL;
> -}
> -
> -int image_cache_hit(ImageCache *cache, uint64_t id)
> -{
> -    ImageCacheItem *item;
> -    if (!(item = image_cache_find(cache, id))) {
> -        return FALSE;
> -    }
> -#ifdef IMAGE_CACHE_AGE
> -    item->age = cache->age;
> -#endif
> -    ring_remove(&item->lru_link);
> -    ring_add(&cache->lru, &item->lru_link);
> -    return TRUE;
> -}
> -
> -static void image_cache_remove(ImageCache *cache, ImageCacheItem *item)
> -{
> -    ImageCacheItem **now;
> -
> -    now = &cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
> -    for (;;) {
> -        spice_assert(*now);
> -        if (*now == item) {
> -            *now = item->next;
> -            break;
> -        }
> -        now = &(*now)->next;
> -    }
> -    ring_remove(&item->lru_link);
> -    pixman_image_unref(item->image);
> -    free(item);
> -#ifndef IMAGE_CACHE_AGE
> -    cache->num_items--;
> -#endif
> -}
> -
> -#define IMAGE_CACHE_MAX_ITEMS 2
> -
> -static void image_cache_put(SpiceImageCache *spice_cache, uint64_t id, pixman_image_t *image)
> -{
> -    ImageCache *cache = (ImageCache *)spice_cache;
> -    ImageCacheItem *item;
> -
> -#ifndef IMAGE_CACHE_AGE
> -    if (cache->num_items == IMAGE_CACHE_MAX_ITEMS) {
> -        ImageCacheItem *tail = (ImageCacheItem *)ring_get_tail(&cache->lru);
> -        spice_assert(tail);
> -        image_cache_remove(cache, tail);
> -    }
> -#endif
> -
> -    item = spice_new(ImageCacheItem, 1);
> -    item->id = id;
> -#ifdef IMAGE_CACHE_AGE
> -    item->age = cache->age;
> -#else
> -    cache->num_items++;
> -#endif
> -    item->image = pixman_image_ref(image);
> -    ring_item_init(&item->lru_link);
> -
> -    item->next = cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
> -    cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE] = item;
> -
> -    ring_add(&cache->lru, &item->lru_link);
> -}
> -
> -static pixman_image_t *image_cache_get(SpiceImageCache *spice_cache, uint64_t id)
> -{
> -    ImageCache *cache = (ImageCache *)spice_cache;
> -
> -    ImageCacheItem *item = image_cache_find(cache, id);
> -    if (!item) {
> -        spice_error("not found");
> -    }
> -    return pixman_image_ref(item->image);
> -}
> -
> -void image_cache_init(ImageCache *cache)
> -{
> -    static SpiceImageCacheOps image_cache_ops = {
> -        image_cache_put,
> -        image_cache_get,
> -    };
> -
> -    cache->base.ops = &image_cache_ops;
> -    memset(cache->hash_table, 0, sizeof(cache->hash_table));
> -    ring_init(&cache->lru);
> -#ifdef IMAGE_CACHE_AGE
> -    cache->age = 0;
> -#else
> -    cache->num_items = 0;
> -#endif
> -}
> -
> -void image_cache_reset(ImageCache *cache)
> -{
> -    ImageCacheItem *item;
> -
> -    while ((item = (ImageCacheItem *)ring_get_head(&cache->lru))) {
> -        image_cache_remove(cache, item);
> -    }
> -#ifdef IMAGE_CACHE_AGE
> -    cache->age = 0;
> -#endif
> -}
> -
> -#define IMAGE_CACHE_DEPTH 4
> -
> -void image_cache_aging(ImageCache *cache)
> -{
> -#ifdef IMAGE_CACHE_AGE
> -    ImageCacheItem *item;
> -
> -    cache->age++;
> -    while ((item = (ImageCacheItem *)ring_get_tail(&cache->lru)) &&
> -           cache->age - item->age > IMAGE_CACHE_DEPTH) {
> -        image_cache_remove(cache, item);
> -    }
> -#endif
> -}
> -
> -void image_cache_localize(ImageCache *cache, SpiceImage **image_ptr,
> -                          SpiceImage *image_store, Drawable *drawable)
> -{
> -    SpiceImage *image = *image_ptr;
> -
> -    if (image == NULL) {
> -        spice_assert(drawable != NULL);
> -        spice_assert(drawable->red_drawable->self_bitmap_image != NULL);
> -        *image_ptr = drawable->red_drawable->self_bitmap_image;
> -        return;
> -    }
> -
> -    if (image_cache_hit(cache, image->descriptor.id)) {
> -        image_store->descriptor = image->descriptor;
> -        image_store->descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE;
> -        image_store->descriptor.flags = 0;
> -        *image_ptr = image_store;
> -        return;
> -    }
> -
> -    switch (image->descriptor.type) {
> -    case SPICE_IMAGE_TYPE_QUIC: {
> -        image_store->descriptor = image->descriptor;
> -        image_store->u.quic = image->u.quic;
> -        *image_ptr = image_store;
> -#ifdef IMAGE_CACHE_AGE
> -        image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
> -#else
> -        if (image_store->descriptor.width * image->descriptor.height >= 640 * 480) {
> -            image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
> -        }
> -#endif
> -        break;
> -    }
> -    case SPICE_IMAGE_TYPE_BITMAP:
> -    case SPICE_IMAGE_TYPE_SURFACE:
> -        /* nothing */
> -        break;
> -    default:
> -        spice_error("invalid image type");
> -    }
> -}
> -
> -void image_cache_localize_brush(ImageCache *cache, SpiceBrush *brush, SpiceImage *image_store)
> -{
> -    if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
> -        image_cache_localize(cache, &brush->u.pattern.pat, image_store, NULL);
> -    }
> -}
> -
> -void image_cache_localize_mask(ImageCache *cache, SpiceQMask *mask, SpiceImage *image_store)
> -{
> -    if (mask->bitmap) {
> -        image_cache_localize(cache, &mask->bitmap, image_store, NULL);
> -    }
> -}
> diff --git a/server/spice_image_cache.h b/server/spice_image_cache.h
> deleted file mode 100644
> index 6d6b32d..0000000
> --- a/server/spice_image_cache.h
> +++ /dev/null
> @@ -1,65 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2009-2015 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifndef H_SPICE_IMAGE_CACHE
> -#define H_SPICE_IMAGE_CACHE
> -
> -#include <inttypes.h>
> -
> -#include "common/pixman_utils.h"
> -#include "common/canvas_base.h"
> -#include "common/ring.h"
> -
> -/* FIXME: move back to display_channel.h (once structs are private) */
> -typedef struct Drawable Drawable;
> -typedef struct DisplayChannelClient DisplayChannelClient;
> -
> -typedef struct ImageCacheItem {
> -    RingItem lru_link;
> -    uint64_t id;
> -#ifdef IMAGE_CACHE_AGE
> -    uint32_t age;
> -#endif
> -    struct ImageCacheItem *next;
> -    pixman_image_t *image;
> -} ImageCacheItem;
> -
> -#define IMAGE_CACHE_HASH_SIZE 1024
> -
> -typedef struct ImageCache {
> -    SpiceImageCache base;
> -    ImageCacheItem *hash_table[IMAGE_CACHE_HASH_SIZE];
> -    Ring lru;
> -#ifdef IMAGE_CACHE_AGE
> -    uint32_t age;
> -#else
> -    uint32_t num_items;
> -#endif
> -} ImageCache;
> -
> -int          image_cache_hit               (ImageCache *cache, uint64_t id);
> -void         image_cache_init              (ImageCache *cache);
> -void         image_cache_reset             (ImageCache *cache);
> -void         image_cache_aging             (ImageCache *cache);
> -void         image_cache_localize          (ImageCache *cache, SpiceImage **image_ptr,
> -                                            SpiceImage *image_store, Drawable *drawable);
> -void         image_cache_localize_brush    (ImageCache *cache, SpiceBrush *brush,
> -                                            SpiceImage *image_store);
> -void         image_cache_localize_mask     (ImageCache *cache, SpiceQMask *mask,
> -                                            SpiceImage *image_store);
> -
> -#endif
> diff --git a/server/spicevmc.c b/server/spicevmc.c
> index d37b1ec..52a29a4 100644
> --- a/server/spicevmc.c
> +++ b/server/spicevmc.c
> @@ -30,10 +30,10 @@
>
>  #include "common/generated_server_marshallers.h"
>
> -#include "char_device.h"
> +#include "char-device.h"
>  #include "red_channel.h"
>  #include "reds.h"
> -#include "migration_protocol.h"
> +#include "migration-protocol.h"
>
>  /* todo: add flow control. i.e.,
>   * (a) limit the tokens available for the client
> diff --git a/server/stream.h b/server/stream.h
> index 214d1df..cb2b844 100644
> --- a/server/stream.h
> +++ b/server/stream.h
> @@ -20,10 +20,10 @@
>
>  #include <glib.h>
>  #include "utils.h"
> -#include "mjpeg_encoder.h"
> +#include "mjpeg-encoder.h"
>  #include "common/region.h"
>  #include "red_channel.h"
> -#include "spice_image_cache.h"
> +#include "image-cache.h"
>
>  #define RED_STREAM_DETACTION_MAX_DELTA ((1000 * 1000 * 1000) / 5) // 1/5 sec
>  #define RED_STREAM_CONTINUS_MAX_DELTA (1000 * 1000 * 1000)
> diff --git a/server/sw-canvas.c b/server/sw-canvas.c
> new file mode 100644
> index 0000000..0ef050e
> --- /dev/null
> +++ b/server/sw-canvas.c
> @@ -0,0 +1,26 @@
> +/*
> +   Copyright (C) 2011 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include "common/spice_common.h"
> +
> +#include "sw-canvas.h"
> +#define SW_CANVAS_IMAGE_CACHE
> +#include "common/sw_canvas.c"
> +#undef SW_CANVAS_IMAGE_CACHE
> diff --git a/server/sw-canvas.h b/server/sw-canvas.h
> new file mode 100644
> index 0000000..5ffba3d
> --- /dev/null
> +++ b/server/sw-canvas.h
> @@ -0,0 +1,24 @@
> +/*
> +   Copyright (C) 2011 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifndef SW_CANVAS_H_
> +#define SW_CANVAS_H_
> +
> +#define SW_CANVAS_IMAGE_CACHE
> +#include "common/sw_canvas.h"
> +#undef SW_CANVAS_IMAGE_CACHE
> +
> +#endif
> diff --git a/server/zlib-encoder.c b/server/zlib-encoder.c
> new file mode 100644
> index 0000000..069a448
> --- /dev/null
> +++ b/server/zlib-encoder.c
> @@ -0,0 +1,125 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2010 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include "red_common.h"
> +#include "zlib-encoder.h"
> +#include <zlib.h>
> +
> +struct ZlibEncoder {
> +    ZlibEncoderUsrContext *usr;
> +
> +    z_stream strm;
> +    int last_level;
> +};
> +
> +ZlibEncoder* zlib_encoder_create(ZlibEncoderUsrContext *usr, int level)
> +{
> +    ZlibEncoder *enc;
> +    int z_ret;
> +
> +    if (!usr->more_space || !usr->more_input) {
> +        return NULL;
> +    }
> +
> +    enc = spice_new0(ZlibEncoder, 1);
> +
> +    enc->usr = usr;
> +
> +    enc->strm.zalloc = Z_NULL;
> +    enc->strm.zfree = Z_NULL;
> +    enc->strm.opaque = Z_NULL;
> +
> +    z_ret = deflateInit(&enc->strm, level);
> +    enc->last_level = level;
> +    if (z_ret != Z_OK) {
> +        spice_printerr("zlib error");
> +        free(enc);
> +        return NULL;
> +    }
> +
> +    return enc;
> +}
> +
> +void zlib_encoder_destroy(ZlibEncoder *encoder)
> +{
> +    deflateEnd(&encoder->strm);
> +    free(encoder);
> +}
> +
> +/* returns the total size of the encoded data */
> +int zlib_encode(ZlibEncoder *zlib, int level, int input_size,
> +                uint8_t *io_ptr, unsigned int num_io_bytes)
> +{
> +    int flush;
> +    int enc_size = 0;
> +    int out_size = 0;
> +    int z_ret;
> +
> +    z_ret = deflateReset(&zlib->strm);
> +
> +    if (z_ret != Z_OK) {
> +        spice_error("deflateReset failed");
> +    }
> +
> +    zlib->strm.next_out = io_ptr;
> +    zlib->strm.avail_out = num_io_bytes;
> +
> +    if (level != zlib->last_level) {
> +        if (zlib->strm.avail_out == 0) {
> +            zlib->strm.avail_out = zlib->usr->more_space(zlib->usr, &zlib->strm.next_out);
> +            if (zlib->strm.avail_out == 0) {
> +                spice_error("not enough space");
> +            }
> +        }
> +        z_ret = deflateParams(&zlib->strm, level, Z_DEFAULT_STRATEGY);
> +        if (z_ret != Z_OK) {
> +            spice_error("deflateParams failed");
> +        }
> +        zlib->last_level = level;
> +    }
> +
> +
> +    do {
> +        zlib->strm.avail_in = zlib->usr->more_input(zlib->usr, &zlib->strm.next_in);
> +        if (zlib->strm.avail_in <= 0) {
> +            spice_error("more input failed");
> +        }
> +        enc_size += zlib->strm.avail_in;
> +        flush = (enc_size == input_size) ?  Z_FINISH : Z_NO_FLUSH;
> +        while (1) {
> +            int deflate_size = zlib->strm.avail_out;
> +            z_ret = deflate(&zlib->strm, flush);
> +            spice_assert(z_ret != Z_STREAM_ERROR);
> +            out_size += deflate_size - zlib->strm.avail_out;
> +            if (zlib->strm.avail_out) {
> +                break;
> +            }
> +
> +            zlib->strm.avail_out = zlib->usr->more_space(zlib->usr, &zlib->strm.next_out);
> +            if (zlib->strm.avail_out == 0) {
> +                spice_error("not enough space");
> +            }
> +        }
> +    } while (flush != Z_FINISH);
> +
> +    spice_assert(z_ret == Z_STREAM_END);
> +    return out_size;
> +}
> diff --git a/server/zlib-encoder.h b/server/zlib-encoder.h
> new file mode 100644
> index 0000000..0620fc7
> --- /dev/null
> +++ b/server/zlib-encoder.h
> @@ -0,0 +1,47 @@
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.
> +
> +   Redistribution and use in source and binary forms, with or without
> +   modification, are permitted provided that the following conditions are
> +   met:
> +
> +       * Redistributions of source code must retain the above copyright
> +         notice, this list of conditions and the following disclaimer.
> +       * Redistributions in binary form must reproduce the above copyright
> +         notice, this list of conditions and the following disclaimer in
> +         the documentation and/or other materials provided with the
> +         distribution.
> +       * Neither the name of the copyright holder nor the names of its
> +         contributors may be used to endorse or promote products derived
> +         from this software without specific prior written permission.
> +
> +   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
> +   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
> +   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
> +   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> +   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> +   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> +   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> +   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> +   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> +   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> +   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> +*/
> +#ifndef _H_ZLIB_ENCODER
> +#define _H_ZLIB_ENCODER
> +
> +typedef struct ZlibEncoder ZlibEncoder;
> +typedef struct ZlibEncoderUsrContext ZlibEncoderUsrContext;
> +
> +struct ZlibEncoderUsrContext {
> +    int (*more_space)(ZlibEncoderUsrContext *usr, uint8_t **io_ptr);
> +    int (*more_input)(ZlibEncoderUsrContext *usr, uint8_t **input);
> +};
> +
> +ZlibEncoder* zlib_encoder_create(ZlibEncoderUsrContext *usr, int level);
> +void zlib_encoder_destroy(ZlibEncoder *encoder);
> +
> +/* returns the total size of the encoded data */
> +int zlib_encode(ZlibEncoder *zlib, int level, int input_size,
> +                uint8_t *io_ptr, unsigned int num_io_bytes);
> +#endif
> diff --git a/server/zlib_encoder.c b/server/zlib_encoder.c
> deleted file mode 100644
> index a3d2aa6..0000000
> --- a/server/zlib_encoder.c
> +++ /dev/null
> @@ -1,125 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> -   Copyright (C) 2010 Red Hat, Inc.
> -
> -   This library is free software; you can redistribute it and/or
> -   modify it under the terms of the GNU Lesser General Public
> -   License as published by the Free Software Foundation; either
> -   version 2.1 of the License, or (at your option) any later version.
> -
> -   This library 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
> -   Lesser General Public License for more details.
> -
> -   You should have received a copy of the GNU Lesser General Public
> -   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> -*/
> -#ifdef HAVE_CONFIG_H
> -#include <config.h>
> -#endif
> -
> -#include "red_common.h"
> -#include "zlib_encoder.h"
> -#include <zlib.h>
> -
> -struct ZlibEncoder {
> -    ZlibEncoderUsrContext *usr;
> -
> -    z_stream strm;
> -    int last_level;
> -};
> -
> -ZlibEncoder* zlib_encoder_create(ZlibEncoderUsrContext *usr, int level)
> -{
> -    ZlibEncoder *enc;
> -    int z_ret;
> -
> -    if (!usr->more_space || !usr->more_input) {
> -        return NULL;
> -    }
> -
> -    enc = spice_new0(ZlibEncoder, 1);
> -
> -    enc->usr = usr;
> -
> -    enc->strm.zalloc = Z_NULL;
> -    enc->strm.zfree = Z_NULL;
> -    enc->strm.opaque = Z_NULL;
> -
> -    z_ret = deflateInit(&enc->strm, level);
> -    enc->last_level = level;
> -    if (z_ret != Z_OK) {
> -        spice_printerr("zlib error");
> -        free(enc);
> -        return NULL;
> -    }
> -
> -    return enc;
> -}
> -
> -void zlib_encoder_destroy(ZlibEncoder *encoder)
> -{
> -    deflateEnd(&encoder->strm);
> -    free(encoder);
> -}
> -
> -/* returns the total size of the encoded data */
> -int zlib_encode(ZlibEncoder *zlib, int level, int input_size,
> -                uint8_t *io_ptr, unsigned int num_io_bytes)
> -{
> -    int flush;
> -    int enc_size = 0;
> -    int out_size = 0;
> -    int z_ret;
> -
> -    z_ret = deflateReset(&zlib->strm);
> -
> -    if (z_ret != Z_OK) {
> -        spice_error("deflateReset failed");
> -    }
> -
> -    zlib->strm.next_out = io_ptr;
> -    zlib->strm.avail_out = num_io_bytes;
> -
> -    if (level != zlib->last_level) {
> -        if (zlib->strm.avail_out == 0) {
> -            zlib->strm.avail_out = zlib->usr->more_space(zlib->usr, &zlib->strm.next_out);
> -            if (zlib->strm.avail_out == 0) {
> -                spice_error("not enough space");
> -            }
> -        }
> -        z_ret = deflateParams(&zlib->strm, level, Z_DEFAULT_STRATEGY);
> -        if (z_ret != Z_OK) {
> -            spice_error("deflateParams failed");
> -        }
> -        zlib->last_level = level;
> -    }
> -
> -
> -    do {
> -        zlib->strm.avail_in = zlib->usr->more_input(zlib->usr, &zlib->strm.next_in);
> -        if (zlib->strm.avail_in <= 0) {
> -            spice_error("more input failed");
> -        }
> -        enc_size += zlib->strm.avail_in;
> -        flush = (enc_size == input_size) ?  Z_FINISH : Z_NO_FLUSH;
> -        while (1) {
> -            int deflate_size = zlib->strm.avail_out;
> -            z_ret = deflate(&zlib->strm, flush);
> -            spice_assert(z_ret != Z_STREAM_ERROR);
> -            out_size += deflate_size - zlib->strm.avail_out;
> -            if (zlib->strm.avail_out) {
> -                break;
> -            }
> -
> -            zlib->strm.avail_out = zlib->usr->more_space(zlib->usr, &zlib->strm.next_out);
> -            if (zlib->strm.avail_out == 0) {
> -                spice_error("not enough space");
> -            }
> -        }
> -    } while (flush != Z_FINISH);
> -
> -    spice_assert(z_ret == Z_STREAM_END);
> -    return out_size;
> -}
> diff --git a/server/zlib_encoder.h b/server/zlib_encoder.h
> deleted file mode 100644
> index 0620fc7..0000000
> --- a/server/zlib_encoder.h
> +++ /dev/null
> @@ -1,47 +0,0 @@
> -/*
> -   Copyright (C) 2009 Red Hat, Inc.
> -
> -   Redistribution and use in source and binary forms, with or without
> -   modification, are permitted provided that the following conditions are
> -   met:
> -
> -       * Redistributions of source code must retain the above copyright
> -         notice, this list of conditions and the following disclaimer.
> -       * Redistributions in binary form must reproduce the above copyright
> -         notice, this list of conditions and the following disclaimer in
> -         the documentation and/or other materials provided with the
> -         distribution.
> -       * Neither the name of the copyright holder nor the names of its
> -         contributors may be used to endorse or promote products derived
> -         from this software without specific prior written permission.
> -
> -   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
> -   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
> -   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
> -   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> -   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> -   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> -   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> -   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> -   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> -   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> -   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> -*/
> -#ifndef _H_ZLIB_ENCODER
> -#define _H_ZLIB_ENCODER
> -
> -typedef struct ZlibEncoder ZlibEncoder;
> -typedef struct ZlibEncoderUsrContext ZlibEncoderUsrContext;
> -
> -struct ZlibEncoderUsrContext {
> -    int (*more_space)(ZlibEncoderUsrContext *usr, uint8_t **io_ptr);
> -    int (*more_input)(ZlibEncoderUsrContext *usr, uint8_t **input);
> -};
> -
> -ZlibEncoder* zlib_encoder_create(ZlibEncoderUsrContext *usr, int level);
> -void zlib_encoder_destroy(ZlibEncoder *encoder);
> -
> -/* returns the total size of the encoded data */
> -int zlib_encode(ZlibEncoder *zlib, int level, int input_size,
> -                uint8_t *io_ptr, unsigned int num_io_bytes);
> -#endif
> --
> 2.4.3
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel

Acked-by: Fabiano FidĂȘncio <fidencio at redhat.com>


More information about the Spice-devel mailing list