[Spice-commits] 15 commits - server/Makefile.am server/char-device.c server/char-device.h server/reds.c server/stream-channel.c server/stream-channel.h server/stream-device.c

Frediano Ziglio fziglio at kemper.freedesktop.org
Mon Oct 16 19:28:56 UTC 2017


 server/Makefile.am      |    3 
 server/char-device.c    |    2 
 server/char-device.h    |    1 
 server/reds.c           |    8 
 server/stream-channel.c |  577 ++++++++++++++++++++++++++++++++++++++++++++++++
 server/stream-channel.h |   82 ++++++
 server/stream-device.c  |  372 ++++++++++++++++++++++++++++++
 7 files changed, 1042 insertions(+), 3 deletions(-)

New commits:
commit 7b7a842a4e8c5fceb9f3c261abf6e4536dc3dd77
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Wed Jun 7 13:13:21 2017 +0100

    stream-channel: Activate streaming report from client
    
    Setting the capability is not enough, each stream must be enabled
    so do so if client support them.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/stream-channel.c b/server/stream-channel.c
index ec4bf021..7e15dd36 100644
--- a/server/stream-channel.c
+++ b/server/stream-channel.c
@@ -27,6 +27,7 @@
 #include "reds.h"
 #include "common-graphics-channel.h"
 #include "display-limits.h"
+#include "stream.h" // TODO remove, put common stuff
 
 #define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type()
 
@@ -95,6 +96,7 @@ enum {
     RED_PIPE_ITEM_TYPE_STREAM_CREATE,
     RED_PIPE_ITEM_TYPE_STREAM_DATA,
     RED_PIPE_ITEM_TYPE_STREAM_DESTROY,
+    RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT,
 };
 
 typedef struct StreamCreateItem {
@@ -230,6 +232,20 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
         spice_marshall_msg_display_stream_create(m, &item->stream_create);
         break;
     }
+    case RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT: {
+        if (client->stream_id < 0
+            || !red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_STREAM_REPORT)) {
+            return;
+        }
+        SpiceMsgDisplayStreamActivateReport msg;
+        msg.stream_id = client->stream_id;
+        msg.unique_id = 1; // TODO useful ?
+        msg.max_window_size = RED_STREAM_CLIENT_REPORT_WINDOW;
+        msg.timeout_ms = RED_STREAM_CLIENT_REPORT_TIMEOUT;
+        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT);
+        spice_marshall_msg_display_stream_activate_report(m, &msg);
+        break;
+    }
     case RED_PIPE_ITEM_TYPE_STREAM_DATA: {
         StreamDataItem *item = SPICE_UPCAST(StreamDataItem, pipe_item);
         red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA);
@@ -459,6 +475,9 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt)
     item->stream_create.dest = (SpiceRect) { 0, 0, fmt->width, fmt->height };
     item->stream_create.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL };
     red_channel_pipes_add(red_channel, &item->base);
+
+    // activate stream report if possible
+    red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT);
 }
 
 static inline void
commit a8afcfef6b8e69267fae98dcba0f7755438a01d0
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Wed Apr 19 16:24:54 2017 +0100

    stream-device: Limit sending queue from guest to server
    
    Do not allow the guest to fill host memory.
    Also having a huge queue mainly cause to have a higher video
    latency.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/stream-channel.c b/server/stream-channel.c
index 51b8badf..ec4bf021 100644
--- a/server/stream-channel.c
+++ b/server/stream-channel.c
@@ -71,9 +71,15 @@ struct StreamChannel {
     /* size of the current video stream */
     unsigned width, height;
 
+    StreamQueueStat queue_stat;
+
     /* callback to notify when a stream should be started or stopped */
     stream_channel_start_proc start_cb;
     void *start_opaque;
+
+    /* callback to notify when queue statistics changes */
+    stream_channel_queue_stat_proc queue_cb;
+    void *queue_opaque;
 };
 
 struct StreamChannelClass {
@@ -98,6 +104,7 @@ typedef struct StreamCreateItem {
 
 typedef struct StreamDataItem {
     RedPipeItem base;
+    StreamChannel *channel;
     // NOTE: this must be the last field in the structure
     SpiceMsgDisplayStreamData data;
 } StreamDataItem;
@@ -454,6 +461,27 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt)
     red_channel_pipes_add(red_channel, &item->base);
 }
 
+static inline void
+stream_channel_update_queue_stat(StreamChannel *channel,
+                                 int32_t num_diff, int32_t size_diff)
+{
+    channel->queue_stat.num_items += num_diff;
+    channel->queue_stat.size += size_diff;
+    if (channel->queue_cb) {
+        channel->queue_cb(channel->queue_opaque, &channel->queue_stat, channel);
+    }
+}
+
+static void
+data_item_free(RedPipeItem *base)
+{
+    StreamDataItem *pipe_item = SPICE_UPCAST(StreamDataItem, base);
+
+    stream_channel_update_queue_stat(pipe_item->channel, -1, -pipe_item->data.data_size);
+
+    g_free(pipe_item);
+}
+
 void
 stream_channel_send_data(StreamChannel *channel, const void *data, size_t size, uint32_t mm_time)
 {
@@ -467,10 +495,13 @@ stream_channel_send_data(StreamChannel *channel, const void *data, size_t size,
     RedChannel *red_channel = RED_CHANNEL(channel);
 
     StreamDataItem *item = g_malloc(sizeof(*item) + size);
-    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_STREAM_DATA);
+    red_pipe_item_init_full(&item->base, RED_PIPE_ITEM_TYPE_STREAM_DATA,
+                            data_item_free);
     item->data.base.id = channel->stream_id;
     item->data.base.multi_media_time = mm_time;
     item->data.data_size = size;
+    item->channel = channel;
+    stream_channel_update_queue_stat(channel, 1, size);
     // TODO try to optimize avoiding the copy
     memcpy(item->data.data, data, size);
     red_channel_pipes_add(red_channel, &item->base);
@@ -485,6 +516,14 @@ stream_channel_register_start_cb(StreamChannel *channel,
 }
 
 void
+stream_channel_register_queue_stat_cb(StreamChannel *channel,
+                                      stream_channel_queue_stat_proc cb, void *opaque)
+{
+    channel->queue_cb = cb;
+    channel->queue_opaque = opaque;
+}
+
+void
 stream_channel_reset(StreamChannel *channel)
 {
     struct {
diff --git a/server/stream-channel.h b/server/stream-channel.h
index bd075a95..f961d715 100644
--- a/server/stream-channel.h
+++ b/server/stream-channel.h
@@ -67,6 +67,16 @@ typedef void (*stream_channel_start_proc)(void *opaque, struct StreamMsgStartSto
 void stream_channel_register_start_cb(StreamChannel *channel,
                                       stream_channel_start_proc cb, void *opaque);
 
+typedef struct StreamQueueStat {
+    uint32_t num_items;
+    uint32_t size;
+} StreamQueueStat;
+
+typedef void (*stream_channel_queue_stat_proc)(void *opaque, const StreamQueueStat *stats,
+                                               StreamChannel *channel);
+void stream_channel_register_queue_stat_cb(StreamChannel *channel,
+                                           stream_channel_queue_stat_proc cb, void *opaque);
+
 G_END_DECLS
 
 #endif /* STREAM_CHANNEL_H_ */
diff --git a/server/stream-device.c b/server/stream-device.c
index ae108788..f87538d4 100644
--- a/server/stream-device.c
+++ b/server/stream-device.c
@@ -44,6 +44,7 @@ struct StreamDevice {
     uint8_t hdr_pos;
     bool has_error;
     bool opened;
+    bool flow_stopped;
     StreamChannel *stream_channel;
 };
 
@@ -72,7 +73,7 @@ stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *si
     int n;
     bool handled = false;
 
-    if (dev->has_error || !dev->stream_channel) {
+    if (dev->has_error || dev->flow_stopped || !dev->stream_channel) {
         return NULL;
     }
 
@@ -181,6 +182,9 @@ handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin)
         if (n <= 0) {
             break;
         }
+        // TODO collect all message ??
+        // up: we send a single frame together
+        // down: guest can cause a crash
         stream_channel_send_data(dev->stream_channel, buf, n, reds_get_mm_time());
         dev->hdr.size -= n;
     }
@@ -233,6 +237,33 @@ stream_device_stream_start(void *opaque, StreamMsgStartStop *start,
     red_char_device_write_buffer_add(char_dev, buf);
 }
 
+static void
+stream_device_stream_queue_stat(void *opaque, const StreamQueueStat *stats G_GNUC_UNUSED,
+                                StreamChannel *stream_channel G_GNUC_UNUSED)
+{
+    StreamDevice *dev = (StreamDevice *) opaque;
+
+    if (!dev->opened) {
+        return;
+    }
+
+    // very easy control flow... if any data stop
+    // this seems a very small queue but as we use tcp
+    // there's already that queue
+    if (stats->num_items) {
+        dev->flow_stopped = true;
+        return;
+    }
+
+    if (dev->flow_stopped) {
+        dev->flow_stopped = false;
+        // TODO resume flow...
+        // avoid recursion if we need to call get data from data handling from
+        // data handling
+        red_char_device_wakeup(&dev->parent);
+    }
+}
+
 RedCharDevice *
 stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
 {
@@ -277,6 +308,7 @@ allocate_channels(StreamDevice *dev)
     dev->stream_channel = stream_channel;
 
     stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev);
+    stream_channel_register_queue_stat_cb(stream_channel, stream_device_stream_queue_stat, dev);
 }
 
 static void
@@ -303,6 +335,7 @@ stream_device_port_event(RedCharDevice *char_dev, uint8_t event)
     }
     dev->hdr_pos = 0;
     dev->has_error = false;
+    dev->flow_stopped = false;
     red_char_device_reset(char_dev);
     reset_channels(dev);
 }
commit b7c36361518e6f724fbe5504e3531e497818e763
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Fri Aug 25 00:02:54 2017 +0100

    stream-device: Create channel when needed
    
    This allows a better id allocation as devices are created after
    fixed ones.
    Also will allow to support more easily multiple monitor.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/stream-device.c b/server/stream-device.c
index 9e401f8e..ae108788 100644
--- a/server/stream-device.c
+++ b/server/stream-device.c
@@ -72,7 +72,7 @@ stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *si
     int n;
     bool handled = false;
 
-    if (dev->has_error) {
+    if (dev->has_error || !dev->stream_channel) {
         return NULL;
     }
 
@@ -238,11 +238,7 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
 {
     SpiceCharDeviceInterface *sif;
 
-    StreamChannel *stream_channel = stream_channel_new(reds, 1); // TODO id
-
     StreamDevice *dev = stream_device_new(sin, reds);
-    dev->stream_channel = stream_channel;
-    stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev);
 
     sif = spice_char_device_get_interface(sin);
     if (sif->state) {
@@ -265,6 +261,33 @@ stream_device_dispose(GObject *object)
 }
 
 static void
+allocate_channels(StreamDevice *dev)
+{
+    if (dev->stream_channel) {
+        return;
+    }
+
+    SpiceServer* reds = red_char_device_get_server(RED_CHAR_DEVICE(dev));
+
+    int id = reds_get_free_channel_id(reds, SPICE_CHANNEL_DISPLAY);
+    g_return_if_fail(id >= 0);
+
+    StreamChannel *stream_channel = stream_channel_new(reds, id);
+
+    dev->stream_channel = stream_channel;
+
+    stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev);
+}
+
+static void
+reset_channels(StreamDevice *dev)
+{
+    if (dev->stream_channel) {
+        stream_channel_reset(dev->stream_channel);
+    }
+}
+
+static void
 stream_device_port_event(RedCharDevice *char_dev, uint8_t event)
 {
     if (event != SPICE_PORT_EVENT_OPENED && event != SPICE_PORT_EVENT_CLOSED) {
@@ -275,10 +298,13 @@ stream_device_port_event(RedCharDevice *char_dev, uint8_t event)
 
     // reset device and channel on close/open
     dev->opened = (event == SPICE_PORT_EVENT_OPENED);
+    if (dev->opened) {
+        allocate_channels(dev);
+    }
     dev->hdr_pos = 0;
     dev->has_error = false;
     red_char_device_reset(char_dev);
-    stream_channel_reset(dev->stream_channel);
+    reset_channels(dev);
 }
 
 static void
commit 82769e5dfc024593affcc42af5684eaa5a72804c
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Wed Jan 25 22:42:00 2017 +0000

    stream-device: Start supporting resetting device when close/open on guest
    
    When guest close the device the host device has to be reset too.
    This make easier to restart the guest device which can happen in case
    of reboot, agent issues or if we want to update the agent.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/stream-channel.c b/server/stream-channel.c
index e89563b8..51b8badf 100644
--- a/server/stream-channel.c
+++ b/server/stream-channel.c
@@ -483,3 +483,37 @@ stream_channel_register_start_cb(StreamChannel *channel,
     channel->start_cb = cb;
     channel->start_opaque = opaque;
 }
+
+void
+stream_channel_reset(StreamChannel *channel)
+{
+    struct {
+        StreamMsgStartStop base;
+        uint8_t codecs_buffer[MAX_SUPPORTED_CODECS];
+    } start_msg;
+    StreamMsgStartStop *const start = &start_msg.base;
+    RedChannel *red_channel = RED_CHANNEL(channel);
+
+    // send destroy old stream
+    red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_DESTROY);
+
+    // destroy display surface
+    if (channel->width != 0 && channel->height != 0) {
+        red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY);
+    }
+
+    channel->stream_id = -1;
+    channel->width = 0;
+    channel->height = 0;
+
+    if (!red_channel_is_connected(red_channel)) {
+        return;
+    }
+
+    // try to request a new stream, this should start a new stream
+    // if the guest is connected to the device and a client is already connected
+    start->num_codecs = stream_channel_get_supported_codecs(channel, start->codecs);
+    // send in any case, even if list is not changed
+    // notify device about changes
+    request_new_stream(channel, start);
+}
diff --git a/server/stream-channel.h b/server/stream-channel.h
index ba098df4..bd075a95 100644
--- a/server/stream-channel.h
+++ b/server/stream-channel.h
@@ -48,7 +48,12 @@ GType stream_channel_get_type(void) G_GNUC_CONST;
  */
 StreamChannel* stream_channel_new(RedsState *server, uint32_t id);
 
-struct StreamMsgFormat;
+/**
+ * Reset channel at initial state
+ */
+void stream_channel_reset(StreamChannel *channel);
+
+struct StreamMsgStreamFormat;
 struct StreamMsgStartStop;
 
 void stream_channel_change_format(StreamChannel *channel,
diff --git a/server/stream-device.c b/server/stream-device.c
index 6e78b1a9..9e401f8e 100644
--- a/server/stream-device.c
+++ b/server/stream-device.c
@@ -43,6 +43,7 @@ struct StreamDevice {
     StreamDevHeader hdr;
     uint8_t hdr_pos;
     bool has_error;
+    bool opened;
     StreamChannel *stream_channel;
 };
 
@@ -203,6 +204,35 @@ stream_device_remove_client(RedCharDevice *self, RedClient *client)
 {
 }
 
+static void
+stream_device_stream_start(void *opaque, StreamMsgStartStop *start,
+                           StreamChannel *stream_channel G_GNUC_UNUSED)
+{
+    StreamDevice *dev = (StreamDevice *) opaque;
+
+    if (!dev->opened) {
+        return;
+    }
+
+    int msg_size = sizeof(*start) + sizeof(start->codecs[0]) * start->num_codecs;
+    int total_size = sizeof(StreamDevHeader) + msg_size;
+
+    RedCharDevice *char_dev = RED_CHAR_DEVICE(dev);
+    RedCharDeviceWriteBuffer *buf =
+        red_char_device_write_buffer_get_server_no_token(char_dev, total_size);
+    buf->buf_used = total_size;
+
+    StreamDevHeader *hdr = (StreamDevHeader *)buf->buf;
+    hdr->protocol_version = STREAM_DEVICE_PROTOCOL;
+    hdr->padding = 0;
+    hdr->type = GUINT16_TO_LE(STREAM_TYPE_START_STOP);
+    hdr->size = GUINT32_TO_LE(msg_size);
+
+    memcpy(&hdr[1], start, msg_size);
+
+    red_char_device_write_buffer_add(char_dev, buf);
+}
+
 RedCharDevice *
 stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
 {
@@ -212,6 +242,7 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
 
     StreamDevice *dev = stream_device_new(sin, reds);
     dev->stream_channel = stream_channel;
+    stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev);
 
     sif = spice_char_device_get_interface(sin);
     if (sif->state) {
@@ -234,6 +265,23 @@ stream_device_dispose(GObject *object)
 }
 
 static void
+stream_device_port_event(RedCharDevice *char_dev, uint8_t event)
+{
+    if (event != SPICE_PORT_EVENT_OPENED && event != SPICE_PORT_EVENT_CLOSED) {
+        return;
+    }
+
+    StreamDevice *dev = STREAM_DEVICE(char_dev);
+
+    // reset device and channel on close/open
+    dev->opened = (event == SPICE_PORT_EVENT_OPENED);
+    dev->hdr_pos = 0;
+    dev->has_error = false;
+    red_char_device_reset(char_dev);
+    stream_channel_reset(dev->stream_channel);
+}
+
+static void
 stream_device_class_init(StreamDeviceClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS(klass);
@@ -245,6 +293,7 @@ stream_device_class_init(StreamDeviceClass *klass)
     char_dev_class->send_msg_to_client = stream_device_send_msg_to_client;
     char_dev_class->send_tokens_to_client = stream_device_send_tokens_to_client;
     char_dev_class->remove_client = stream_device_remove_client;
+    char_dev_class->port_event = stream_device_port_event;
 }
 
 static void
commit 7131e353005e7f48423d7648a844fa9fbd13f4fb
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Tue Jan 24 11:36:27 2017 +0000

    char-device: Do not stop and clear interface on reset
    
    Currently, red_char_device_reset() stops the device, clears all pending
    messages, and clears its device instance. After this function is called,
    the char device will not work again until it is assigned a new device
    instance and restarted. This is fine for the vdagent char device, which
    is currently the only user of this function. But for the stream device,
    we want to be able to reset the char device to a working state (e.g.
    clear all pending messages, etc) without stopping or disabling the char
    device. So this function will now only reset the char device to a clean
    working state, and the _stop() and _reset_dev_instance() calls will be
    moved up to the caller.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/char-device.c b/server/char-device.c
index ef361b7a..2f8d3821 100644
--- a/server/char-device.c
+++ b/server/char-device.c
@@ -823,7 +823,6 @@ void red_char_device_reset(RedCharDevice *dev)
     GList *client_item;
     RedCharDeviceWriteBuffer *buf;
 
-    red_char_device_stop(dev);
     dev->priv->wait_for_migrate_data = FALSE;
     spice_debug("char device %p", dev);
     while ((buf = g_queue_pop_tail(&dev->priv->write_queue))) {
@@ -845,7 +844,6 @@ void red_char_device_reset(RedCharDevice *dev)
         dev_client->num_client_tokens += dev_client->num_client_tokens_free;
         dev_client->num_client_tokens_free = 0;
     }
-    red_char_device_reset_dev_instance(dev, NULL);
 }
 
 void red_char_device_wakeup(RedCharDevice *dev)
diff --git a/server/reds.c b/server/reds.c
index 49a400b8..4af80406 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -470,6 +470,7 @@ static void reds_reset_vdp(RedsState *reds)
 {
     RedCharDeviceVDIPort *dev = reds->agent_dev;
     SpiceCharDeviceInterface *sif;
+    RedCharDevice *char_dev;
 
     dev->priv->read_state = VDI_PORT_READ_STATE_READ_HEADER;
     dev->priv->receive_pos = (uint8_t *)&dev->priv->vdi_chunk_header;
@@ -502,7 +503,10 @@ static void reds_reset_vdp(RedsState *reds)
      *  The tokens are also reset to avoid mismatch in upon agent reconnection.
      */
     dev->priv->agent_attached = FALSE;
-    red_char_device_reset(RED_CHAR_DEVICE(dev));
+    char_dev = RED_CHAR_DEVICE(dev);
+    red_char_device_stop(char_dev);
+    red_char_device_reset(char_dev);
+    red_char_device_reset_dev_instance(char_dev, NULL);
 
     sif = spice_char_device_get_interface(reds->vdagent);
     if (sif->state) {
commit 9ea16ff4a0a7c8e17ff49dce0011019049bfde84
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Sat Jan 21 09:01:18 2017 +0000

    stream-channel: Do not show an empty blank screen on start
    
    Start showing something when we have a surface and stream
    instead of showing a blank screen which is now not useful.
    Was useful for debugging purposes to understand that the
    new channel was sending messages correctly to client and
    client could handle them.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/stream-channel.c b/server/stream-channel.c
index 2ad9ebae..e89563b8 100644
--- a/server/stream-channel.c
+++ b/server/stream-channel.c
@@ -360,8 +360,13 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea
     red_channel_client_ack_zero_messages_window(rcc);
 
     // "emulate" dcc_start
-    // TODO only if "surface"
     red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
+
+    // only if "surface"
+    if (channel->width == 0 || channel->height == 0) {
+        return;
+    }
+
     // pass proper data
     red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SURFACE_CREATE);
     // surface data
@@ -407,8 +412,8 @@ static void
 stream_channel_init(StreamChannel *channel)
 {
     channel->stream_id = -1;
-    channel->width = 1024;
-    channel->height = 768;
+    channel->width = 0;
+    channel->height = 0;
 }
 
 void
@@ -421,11 +426,14 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt)
 
     // send new create surface if required
     if (channel->width != fmt->width || channel->height != fmt->height) {
+        if (channel->width != 0 && channel->height != 0) {
+            red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY);
+        }
         channel->width = fmt->width;
         channel->height = fmt->height;
-        red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY);
         red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_CREATE);
         // TODO monitors config ??
+        red_channel_pipes_add_empty_msg(red_channel, SPICE_MSG_DISPLAY_MARK);
     }
 
     // allocate a new stream id
commit 633e04b40c3b4a51abe4971a5123b1d3493aefac
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Sat Jan 21 19:03:19 2017 +0000

    stream-channel: Support client connection/disconnection
    
    When a new client is connected we must restart the stream so new
    clients can receive correct data without having to wait for the
    next full screen (which on idle screen could take ages).
    On disconnection we should tell the guest to stop streaming
    not wasting resources to stream not needed data.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/stream-channel.c b/server/stream-channel.c
index be6f7c7d..2ad9ebae 100644
--- a/server/stream-channel.c
+++ b/server/stream-channel.c
@@ -121,8 +121,32 @@ stream_channel_client_init(StreamChannelClient *client)
 }
 
 static void
+request_new_stream(StreamChannel *channel, StreamMsgStartStop *start)
+{
+    if (channel->start_cb) {
+        channel->start_cb(channel->start_opaque, start, channel);
+    }
+}
+
+static void
 stream_channel_client_on_disconnect(RedChannelClient *rcc)
 {
+    RedChannel *red_channel = red_channel_client_get_channel(rcc);
+
+    // if there are still some client connected keep streaming
+    // TODO, maybe would be worth sending new codecs if they are better
+    if (red_channel_is_connected(red_channel)) {
+        return;
+    }
+
+    StreamChannel *channel = STREAM_CHANNEL(red_channel);
+    channel->stream_id = -1;
+    channel->width = 0;
+    channel->height = 0;
+
+    // send stream stop to device
+    StreamMsgStartStop stop = { 0, };
+    request_new_stream(channel, &stop);
 }
 
 static StreamChannelClient*
@@ -258,18 +282,75 @@ stream_channel_new(RedsState *server, uint32_t id)
                         NULL);
 }
 
+#define MAX_SUPPORTED_CODECS SPICE_VIDEO_CODEC_TYPE_ENUM_END
+
+// find common codecs supported by all clients
+static uint8_t
+stream_channel_get_supported_codecs(StreamChannel *channel, uint8_t *out_codecs)
+{
+    RedChannelClient *rcc;
+    int codec;
+
+    static const uint16_t codec2cap[] = {
+        0, // invalid
+        SPICE_DISPLAY_CAP_CODEC_MJPEG,
+        SPICE_DISPLAY_CAP_CODEC_VP8,
+        SPICE_DISPLAY_CAP_CODEC_H264,
+        SPICE_DISPLAY_CAP_CODEC_VP9,
+    };
+
+    bool supported[SPICE_N_ELEMENTS(codec2cap)];
+
+    for (codec = 0; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
+        supported[codec] = true;
+    }
+
+    FOREACH_CLIENT(channel, rcc) {
+        for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
+            // if do not support codec delete from list
+            if (!red_channel_client_test_remote_cap(rcc, codec2cap[codec])) {
+                supported[codec] = false;
+            }
+        }
+    }
+
+    // surely mjpeg is supported
+    supported[SPICE_VIDEO_CODEC_TYPE_MJPEG] = true;
+
+    int num = 0;
+    for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
+        if (supported[codec]) {
+            out_codecs[num++] = codec;
+        }
+    }
+
+    return num;
+}
+
 static void
 stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStream *stream,
                        int migration, RedChannelCapabilities *caps)
 {
     StreamChannel *channel = STREAM_CHANNEL(red_channel);
     StreamChannelClient *client;
+    struct {
+        StreamMsgStartStop base;
+        uint8_t codecs_buffer[MAX_SUPPORTED_CODECS];
+    } start_msg;
+    StreamMsgStartStop *const start = &start_msg.base;
 
     spice_return_if_fail(stream != NULL);
 
     client = stream_channel_client_new(channel, red_client, stream, migration, caps);
     spice_return_if_fail(client != NULL);
 
+    // request new stream
+    start->num_codecs = stream_channel_get_supported_codecs(channel, start->codecs);
+    // send in any case, even if list is not changed
+    // notify device about changes
+    request_new_stream(channel, start);
+
+
     // TODO set capabilities like  SPICE_DISPLAY_CAP_MONITORS_CONFIG
     // see guest_set_client_capabilities
     RedChannelClient *rcc = RED_CHANNEL_CLIENT(client);
commit 3cf09bd5b37bbaaba525933bde320e64bfa057b9
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Sat Jan 21 19:08:12 2017 +0000

    stream-channel: Allows to register callback to get new stream request
    
    The channel needs to communicate when it receive a new
    stream request from the guest.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/stream-channel.c b/server/stream-channel.c
index 59f566f5..be6f7c7d 100644
--- a/server/stream-channel.c
+++ b/server/stream-channel.c
@@ -70,6 +70,10 @@ struct StreamChannel {
     int stream_id;
     /* size of the current video stream */
     unsigned width, height;
+
+    /* callback to notify when a stream should be started or stopped */
+    stream_channel_start_proc start_cb;
+    void *start_opaque;
 };
 
 struct StreamChannelClass {
@@ -382,3 +386,11 @@ stream_channel_send_data(StreamChannel *channel, const void *data, size_t size,
     memcpy(item->data.data, data, size);
     red_channel_pipes_add(red_channel, &item->base);
 }
+
+void
+stream_channel_register_start_cb(StreamChannel *channel,
+                                 stream_channel_start_proc cb, void *opaque)
+{
+    channel->start_cb = cb;
+    channel->start_opaque = opaque;
+}
diff --git a/server/stream-channel.h b/server/stream-channel.h
index 156a75d3..ba098df4 100644
--- a/server/stream-channel.h
+++ b/server/stream-channel.h
@@ -49,6 +49,7 @@ GType stream_channel_get_type(void) G_GNUC_CONST;
 StreamChannel* stream_channel_new(RedsState *server, uint32_t id);
 
 struct StreamMsgFormat;
+struct StreamMsgStartStop;
 
 void stream_channel_change_format(StreamChannel *channel,
                                   const struct StreamMsgFormat *fmt);
@@ -56,6 +57,11 @@ void stream_channel_send_data(StreamChannel *channel,
                               const void *data, size_t size,
                               uint32_t mm_time);
 
+typedef void (*stream_channel_start_proc)(void *opaque, struct StreamMsgStartStop *start,
+                                          StreamChannel *channel);
+void stream_channel_register_start_cb(StreamChannel *channel,
+                                      stream_channel_start_proc cb, void *opaque);
+
 G_END_DECLS
 
 #endif /* STREAM_CHANNEL_H_ */
commit bc906737bc4e01c8a7413da2dae9457961004c03
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Sat Jan 21 19:03:11 2017 +0000

    stream-channel: Allows not fixed size
    
    Remove the fixed size stream and support any display size.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/stream-channel.c b/server/stream-channel.c
index 8931d879..59f566f5 100644
--- a/server/stream-channel.c
+++ b/server/stream-channel.c
@@ -68,6 +68,8 @@ struct StreamChannel {
     /* current video stream id, <0 if not initialized or
      * we are not sending a stream */
     int stream_id;
+    /* size of the current video stream */
+    unsigned width, height;
 };
 
 struct StreamChannelClass {
@@ -78,6 +80,7 @@ G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL)
 
 enum {
     RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST,
+    RED_PIPE_ITEM_TYPE_SURFACE_DESTROY,
     RED_PIPE_ITEM_TYPE_FILL_SURFACE,
     RED_PIPE_ITEM_TYPE_STREAM_CREATE,
     RED_PIPE_ITEM_TYPE_STREAM_DATA,
@@ -137,12 +140,12 @@ stream_channel_client_new(StreamChannel *channel, RedClient *client, RedsStream
 }
 
 static void
-fill_base(SpiceMarshaller *m)
+fill_base(SpiceMarshaller *m, const StreamChannel *channel)
 {
     SpiceMsgDisplayBase base;
 
     base.surface_id = PRIMARY_SURFACE_ID;
-    base.box = (SpiceRect) { 0, 0, 1024, 768 };
+    base.box = (SpiceRect) { 0, 0, channel->width, channel->height };
     base.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL };
 
     spice_marshall_DisplayBase(m, &base);
@@ -153,22 +156,29 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
 {
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     StreamChannelClient *client = STREAM_CHANNEL_CLIENT(rcc);
+    StreamChannel *channel = STREAM_CHANNEL(red_channel_client_get_channel(rcc));
 
     switch (pipe_item->type) {
     case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: {
         red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE);
         SpiceMsgSurfaceCreate surface_create = {
             PRIMARY_SURFACE_ID,
-            1024, 768,
+            channel->width, channel->height,
             SPICE_SURFACE_FMT_32_xRGB, SPICE_SURFACE_FLAGS_PRIMARY
         };
         spice_marshall_msg_display_surface_create(m, &surface_create);
         break;
     }
+    case RED_PIPE_ITEM_TYPE_SURFACE_DESTROY: {
+        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_DESTROY);
+        SpiceMsgSurfaceDestroy surface_destroy = { PRIMARY_SURFACE_ID };
+        spice_marshall_msg_display_surface_destroy(m, &surface_destroy);
+        break;
+    }
     case RED_PIPE_ITEM_TYPE_FILL_SURFACE: {
         red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL);
 
-        fill_base(m);
+        fill_base(m, channel);
 
         SpiceFill fill;
         fill.brush = (SpiceBrush) { SPICE_BRUSH_TYPE_SOLID, { .color = 0 } };
@@ -267,7 +277,7 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea
     // "emulate" dcc_start
     // TODO only if "surface"
     red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
-    // TODO pass proper data
+    // pass proper data
     red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SURFACE_CREATE);
     // surface data
     red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_FILL_SURFACE);
@@ -312,6 +322,8 @@ static void
 stream_channel_init(StreamChannel *channel)
 {
     channel->stream_id = -1;
+    channel->width = 1024;
+    channel->height = 768;
 }
 
 void
@@ -322,7 +334,14 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt)
     // send destroy old stream
     red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_DESTROY);
 
-    // TODO send new create surface if required
+    // send new create surface if required
+    if (channel->width != fmt->width || channel->height != fmt->height) {
+        channel->width = fmt->width;
+        channel->height = fmt->height;
+        red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY);
+        red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_CREATE);
+        // TODO monitors config ??
+    }
 
     // allocate a new stream id
     channel->stream_id = (channel->stream_id + 1) % NUM_STREAMS;
commit 60104d818cd79313c01840bc82c3162eec0f72f4
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Sat Jan 21 10:47:40 2017 +0000

    stream-device: Handle streaming data from device to channel
    
    Handle stream data from device sending to the channel.
    The StreamChannel will forward the data to the clients using standard
    DisplayChannel messages, and will create and destroy streams as
    necessary.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/stream-channel.c b/server/stream-channel.c
index baf3d58a..8931d879 100644
--- a/server/stream-channel.c
+++ b/server/stream-channel.c
@@ -20,11 +20,13 @@
 #endif
 
 #include <common/generated_server_marshallers.h>
+#include <spice/stream-device.h>
 
 #include "red-channel-client.h"
 #include "stream-channel.h"
 #include "reds.h"
 #include "common-graphics-channel.h"
+#include "display-limits.h"
 
 #define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type()
 
@@ -46,6 +48,10 @@ typedef struct StreamChannelClientClass StreamChannelClientClass;
  * to get buffer handling */
 struct StreamChannelClient {
     CommonGraphicsChannelClient parent;
+
+    /* current video stream id, <0 if not initialized or
+     * we are not sending a stream */
+    int stream_id;
 };
 
 struct StreamChannelClientClass {
@@ -58,6 +64,10 @@ G_DEFINE_TYPE(StreamChannelClient, stream_channel_client, TYPE_COMMON_GRAPHICS_C
 
 struct StreamChannel {
     RedChannel parent;
+
+    /* current video stream id, <0 if not initialized or
+     * we are not sending a stream */
+    int stream_id;
 };
 
 struct StreamChannelClass {
@@ -69,8 +79,22 @@ G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL)
 enum {
     RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST,
     RED_PIPE_ITEM_TYPE_FILL_SURFACE,
+    RED_PIPE_ITEM_TYPE_STREAM_CREATE,
+    RED_PIPE_ITEM_TYPE_STREAM_DATA,
+    RED_PIPE_ITEM_TYPE_STREAM_DESTROY,
 };
 
+typedef struct StreamCreateItem {
+    RedPipeItem base;
+    SpiceMsgDisplayStreamCreate stream_create;
+} StreamCreateItem;
+
+typedef struct StreamDataItem {
+    RedPipeItem base;
+    // NOTE: this must be the last field in the structure
+    SpiceMsgDisplayStreamData data;
+} StreamDataItem;
+
 #define PRIMARY_SURFACE_ID 0
 
 static void stream_channel_client_on_disconnect(RedChannelClient *rcc);
@@ -86,6 +110,7 @@ stream_channel_client_class_init(StreamChannelClientClass *klass)
 static void
 stream_channel_client_init(StreamChannelClient *client)
 {
+    client->stream_id = -1;
 }
 
 static void
@@ -127,6 +152,7 @@ static void
 stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
 {
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
+    StreamChannelClient *client = STREAM_CHANNEL_CLIENT(rcc);
 
     switch (pipe_item->type) {
     case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: {
@@ -152,6 +178,32 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
         spice_marshall_Fill(m, &fill, &brush_pat_out, &mask_bitmap_out);
         break;
     }
+    case RED_PIPE_ITEM_TYPE_STREAM_CREATE: {
+        StreamCreateItem *item = SPICE_UPCAST(StreamCreateItem, pipe_item);
+        client->stream_id = item->stream_create.id;
+        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CREATE);
+        spice_marshall_msg_display_stream_create(m, &item->stream_create);
+        break;
+    }
+    case RED_PIPE_ITEM_TYPE_STREAM_DATA: {
+        StreamDataItem *item = SPICE_UPCAST(StreamDataItem, pipe_item);
+        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA);
+        spice_marshall_msg_display_stream_data(m, &item->data);
+        red_pipe_item_ref(pipe_item);
+        spice_marshaller_add_by_ref_full(m, item->data.data, item->data.data_size,
+                                         marshaller_unref_pipe_item, pipe_item);
+        break;
+    }
+    case RED_PIPE_ITEM_TYPE_STREAM_DESTROY: {
+        if (client->stream_id < 0) {
+            return;
+        }
+        SpiceMsgDisplayStreamDestroy stream_destroy = { client->stream_id };
+        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY);
+        spice_marshall_msg_display_stream_destroy(m, &stream_destroy);
+        client->stream_id = -1;
+        break;
+    }
     default:
         spice_error("invalid pipe item type");
     }
@@ -259,4 +311,55 @@ stream_channel_class_init(StreamChannelClass *klass)
 static void
 stream_channel_init(StreamChannel *channel)
 {
+    channel->stream_id = -1;
+}
+
+void
+stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt)
+{
+    RedChannel *red_channel = RED_CHANNEL(channel);
+
+    // send destroy old stream
+    red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_DESTROY);
+
+    // TODO send new create surface if required
+
+    // allocate a new stream id
+    channel->stream_id = (channel->stream_id + 1) % NUM_STREAMS;
+
+    // send create stream
+    StreamCreateItem *item = g_new0(StreamCreateItem, 1);
+    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_STREAM_CREATE);
+    item->stream_create.id = channel->stream_id;
+    item->stream_create.flags = SPICE_STREAM_FLAGS_TOP_DOWN;
+    item->stream_create.codec_type = fmt->codec;
+    item->stream_create.stream_width = fmt->width;
+    item->stream_create.stream_height = fmt->height;
+    item->stream_create.src_width = fmt->width;
+    item->stream_create.src_height = fmt->height;
+    item->stream_create.dest = (SpiceRect) { 0, 0, fmt->width, fmt->height };
+    item->stream_create.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL };
+    red_channel_pipes_add(red_channel, &item->base);
+}
+
+void
+stream_channel_send_data(StreamChannel *channel, const void *data, size_t size, uint32_t mm_time)
+{
+    if (channel->stream_id < 0) {
+        // this condition can happen if the guest didn't handle
+        // the format stop that we send so think the stream is still
+        // started
+        return;
+    }
+
+    RedChannel *red_channel = RED_CHANNEL(channel);
+
+    StreamDataItem *item = g_malloc(sizeof(*item) + size);
+    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_STREAM_DATA);
+    item->data.base.id = channel->stream_id;
+    item->data.base.multi_media_time = mm_time;
+    item->data.data_size = size;
+    // TODO try to optimize avoiding the copy
+    memcpy(item->data.data, data, size);
+    red_channel_pipes_add(red_channel, &item->base);
 }
diff --git a/server/stream-channel.h b/server/stream-channel.h
index e50e17e9..156a75d3 100644
--- a/server/stream-channel.h
+++ b/server/stream-channel.h
@@ -48,6 +48,14 @@ GType stream_channel_get_type(void) G_GNUC_CONST;
  */
 StreamChannel* stream_channel_new(RedsState *server, uint32_t id);
 
+struct StreamMsgFormat;
+
+void stream_channel_change_format(StreamChannel *channel,
+                                  const struct StreamMsgFormat *fmt);
+void stream_channel_send_data(StreamChannel *channel,
+                              const void *data, size_t size,
+                              uint32_t mm_time);
+
 G_END_DECLS
 
 #endif /* STREAM_CHANNEL_H_ */
diff --git a/server/stream-device.c b/server/stream-device.c
index 0c9173ae..6e78b1a9 100644
--- a/server/stream-device.c
+++ b/server/stream-device.c
@@ -164,6 +164,7 @@ handle_msg_format(StreamDevice *dev, SpiceCharDeviceInstance *sin)
     }
     fmt.width = GUINT32_FROM_LE(fmt.width);
     fmt.height = GUINT32_FROM_LE(fmt.height);
+    stream_channel_change_format(dev->stream_channel, &fmt);
 
     return true;
 }
@@ -176,11 +177,10 @@ handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin)
     while (1) {
         uint8_t buf[16 * 1024];
         n = sif->read(sin, buf, sizeof(buf));
-        /* TODO */
-        spice_debug("read %d bytes from device", n);
         if (n <= 0) {
             break;
         }
+        stream_channel_send_data(dev->stream_channel, buf, n, reds_get_mm_time());
         dev->hdr.size -= n;
     }
 
commit 09ddeff804ececf9974a4e66e8a3eee51cc92cf0
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Sat Jan 21 14:02:29 2017 +0000

    stream-device: Create channel for stream device
    
    So can be used by the device to communicate with the clients.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/stream-channel.c b/server/stream-channel.c
index df693651..baf3d58a 100644
--- a/server/stream-channel.c
+++ b/server/stream-channel.c
@@ -19,6 +19,8 @@
 #include <config.h>
 #endif
 
+#include <common/generated_server_marshallers.h>
+
 #include "red-channel-client.h"
 #include "stream-channel.h"
 #include "reds.h"
@@ -64,6 +66,13 @@ struct StreamChannelClass {
 
 G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL)
 
+enum {
+    RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST,
+    RED_PIPE_ITEM_TYPE_FILL_SURFACE,
+};
+
+#define PRIMARY_SURFACE_ID 0
+
 static void stream_channel_client_on_disconnect(RedChannelClient *rcc);
 
 static void
@@ -103,9 +112,46 @@ stream_channel_client_new(StreamChannel *channel, RedClient *client, RedsStream
 }
 
 static void
+fill_base(SpiceMarshaller *m)
+{
+    SpiceMsgDisplayBase base;
+
+    base.surface_id = PRIMARY_SURFACE_ID;
+    base.box = (SpiceRect) { 0, 0, 1024, 768 };
+    base.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL };
+
+    spice_marshall_DisplayBase(m, &base);
+}
+
+static void
 stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
 {
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
+
     switch (pipe_item->type) {
+    case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: {
+        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE);
+        SpiceMsgSurfaceCreate surface_create = {
+            PRIMARY_SURFACE_ID,
+            1024, 768,
+            SPICE_SURFACE_FMT_32_xRGB, SPICE_SURFACE_FLAGS_PRIMARY
+        };
+        spice_marshall_msg_display_surface_create(m, &surface_create);
+        break;
+    }
+    case RED_PIPE_ITEM_TYPE_FILL_SURFACE: {
+        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL);
+
+        fill_base(m);
+
+        SpiceFill fill;
+        fill.brush = (SpiceBrush) { SPICE_BRUSH_TYPE_SOLID, { .color = 0 } };
+        fill.rop_descriptor = SPICE_ROPD_OP_PUT;
+        fill.mask = (SpiceQMask) { 0, { 0, 0 }, NULL };
+        SpiceMarshaller *brush_pat_out, *mask_bitmap_out;
+        spice_marshall_Fill(m, &fill, &brush_pat_out, &mask_bitmap_out);
+        break;
+    }
     default:
         spice_error("invalid pipe item type");
     }
@@ -169,8 +215,10 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea
     // "emulate" dcc_start
     // TODO only if "surface"
     red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
-    // TODO red_surface_create_item_new
-    // TODO surface data ??
+    // TODO pass proper data
+    red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SURFACE_CREATE);
+    // surface data
+    red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_FILL_SURFACE);
     // TODO monitor configs ??
     red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_MARK);
 }
diff --git a/server/stream-device.c b/server/stream-device.c
index 58edb3d1..0c9173ae 100644
--- a/server/stream-device.c
+++ b/server/stream-device.c
@@ -22,6 +22,8 @@
 #include <spice/stream-device.h>
 
 #include "char-device.h"
+#include "stream-channel.h"
+#include "reds.h"
 
 #define TYPE_STREAM_DEVICE stream_device_get_type()
 
@@ -37,9 +39,11 @@ typedef struct StreamDeviceClass StreamDeviceClass;
 
 struct StreamDevice {
     RedCharDevice parent;
+
     StreamDevHeader hdr;
     uint8_t hdr_pos;
     bool has_error;
+    StreamChannel *stream_channel;
 };
 
 struct StreamDeviceClass {
@@ -204,7 +208,10 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
 {
     SpiceCharDeviceInterface *sif;
 
+    StreamChannel *stream_channel = stream_channel_new(reds, 1); // TODO id
+
     StreamDevice *dev = stream_device_new(sin, reds);
+    dev->stream_channel = stream_channel;
 
     sif = spice_char_device_get_interface(sin);
     if (sif->state) {
@@ -217,6 +224,13 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
 static void
 stream_device_dispose(GObject *object)
 {
+    StreamDevice *dev = STREAM_DEVICE(object);
+
+    if (dev->stream_channel) {
+        // close all current connections and drop the reference
+        red_channel_destroy(RED_CHANNEL(dev->stream_channel));
+        dev->stream_channel = NULL;
+    }
 }
 
 static void
commit 14bc15f65657c35f7efb1e965f7af5590c19bdaa
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Tue Mar 14 12:12:22 2017 +0000

    stream-channel: Start implementing DisplayChannel properly
    
    Handle messages from clients.
    Send some messages to clients.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/stream-channel.c b/server/stream-channel.c
index fd735f56..df693651 100644
--- a/server/stream-channel.c
+++ b/server/stream-channel.c
@@ -113,6 +113,25 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
     red_channel_client_begin_send_message(rcc);
 }
 
+static bool
+handle_message(RedChannelClient *rcc, uint16_t type, uint32_t size, void *msg)
+{
+    switch (type) {
+    case SPICE_MSGC_DISPLAY_INIT:
+    case SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION:
+        return true;
+    case SPICE_MSGC_DISPLAY_STREAM_REPORT:
+        /* TODO these will help tune the streaming reducing/increasing quality */
+        return true;
+    case SPICE_MSGC_DISPLAY_GL_DRAW_DONE:
+        /* client should not send this message */
+        return false;
+    default:
+        return red_channel_client_handle_message(rcc, type, size, msg);
+    }
+}
+
+
 StreamChannel*
 stream_channel_new(RedsState *server, uint32_t id)
 {
@@ -138,6 +157,22 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea
 
     client = stream_channel_client_new(channel, red_client, stream, migration, caps);
     spice_return_if_fail(client != NULL);
+
+    // TODO set capabilities like  SPICE_DISPLAY_CAP_MONITORS_CONFIG
+    // see guest_set_client_capabilities
+    RedChannelClient *rcc = RED_CHANNEL_CLIENT(client);
+    red_channel_client_push_set_ack(rcc);
+
+    // TODO what should happen on migration, dcc return if on migration wait ??
+    red_channel_client_ack_zero_messages_window(rcc);
+
+    // "emulate" dcc_start
+    // TODO only if "surface"
+    red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
+    // TODO red_surface_create_item_new
+    // TODO surface data ??
+    // TODO monitor configs ??
+    red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_MARK);
 }
 
 static void
@@ -152,6 +187,10 @@ stream_channel_constructed(GObject *object)
     client_cbs.connect = stream_channel_connect;
     red_channel_register_client_cbs(red_channel, &client_cbs, NULL);
 
+    // TODO, send monitor to support multiple monitors in the future
+//    red_channel_set_cap(red_channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG);
+    red_channel_set_cap(red_channel, SPICE_DISPLAY_CAP_STREAM_REPORT);
+
     reds_register_channel(reds, red_channel);
 }
 
@@ -164,7 +203,7 @@ stream_channel_class_init(StreamChannelClass *klass)
     object_class->constructed = stream_channel_constructed;
 
     channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_DISPLAY, NULL);
-    channel_class->handle_message = red_channel_client_handle_message;
+    channel_class->handle_message = handle_message;
 
     channel_class->send_item = stream_channel_send_item;
 }
commit efcb765d0abd85957bb12b57043970ffb5a79bb1
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Sat Jan 21 15:29:18 2017 +0000

    stream-channel: Write a base channel to implement the streaming
    
    Currently only compile, not used and not much sense
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/Makefile.am b/server/Makefile.am
index f08ddf88..e2e3ce86 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -166,6 +166,8 @@ libserver_la_SOURCES =				\
 	stat.h					\
 	stream.c				\
 	stream.h				\
+	stream-channel.c			\
+	stream-channel.h			\
 	stream-device.c				\
 	sw-canvas.c				\
 	tree.c					\
diff --git a/server/stream-channel.c b/server/stream-channel.c
new file mode 100644
index 00000000..fd735f56
--- /dev/null
+++ b/server/stream-channel.c
@@ -0,0 +1,175 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2017 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-channel-client.h"
+#include "stream-channel.h"
+#include "reds.h"
+#include "common-graphics-channel.h"
+
+#define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type()
+
+#define STREAM_CHANNEL_CLIENT(obj) \
+    (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClient))
+#define STREAM_CHANNEL_CLIENT_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClientClass))
+#define IS_STREAM_CHANNEL_CLIENT(obj) \
+    (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_STREAM_CHANNEL_CLIENT))
+#define IS_STREAM_CHANNEL_CLIENT_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_STREAM_CHANNEL_CLIENT))
+#define STREAM_CHANNEL_CLIENT_GET_CLASS(obj) \
+    (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClientClass))
+
+typedef struct StreamChannelClient StreamChannelClient;
+typedef struct StreamChannelClientClass StreamChannelClientClass;
+
+/* we need to inherit from CommonGraphicsChannelClient
+ * to get buffer handling */
+struct StreamChannelClient {
+    CommonGraphicsChannelClient parent;
+};
+
+struct StreamChannelClientClass {
+    CommonGraphicsChannelClientClass parent_class;
+};
+
+GType stream_channel_client_get_type(void) G_GNUC_CONST;
+
+G_DEFINE_TYPE(StreamChannelClient, stream_channel_client, TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT)
+
+struct StreamChannel {
+    RedChannel parent;
+};
+
+struct StreamChannelClass {
+    RedChannelClass parent_class;
+};
+
+G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL)
+
+static void stream_channel_client_on_disconnect(RedChannelClient *rcc);
+
+static void
+stream_channel_client_class_init(StreamChannelClientClass *klass)
+{
+    RedChannelClientClass *channel_class = RED_CHANNEL_CLIENT_CLASS(klass);
+
+    channel_class->on_disconnect = stream_channel_client_on_disconnect;
+}
+
+static void
+stream_channel_client_init(StreamChannelClient *client)
+{
+}
+
+static void
+stream_channel_client_on_disconnect(RedChannelClient *rcc)
+{
+}
+
+static StreamChannelClient*
+stream_channel_client_new(StreamChannel *channel, RedClient *client, RedsStream *stream,
+                          int mig_target, RedChannelCapabilities *caps)
+{
+    StreamChannelClient *rcc;
+
+    rcc = g_initable_new(TYPE_STREAM_CHANNEL_CLIENT,
+                         NULL, NULL,
+                         "channel", channel,
+                         "client", client,
+                         "stream", stream,
+                         "monitor-latency", FALSE,
+                         "caps", caps,
+                         NULL);
+
+    return rcc;
+}
+
+static void
+stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
+{
+    switch (pipe_item->type) {
+    default:
+        spice_error("invalid pipe item type");
+    }
+
+    red_channel_client_begin_send_message(rcc);
+}
+
+StreamChannel*
+stream_channel_new(RedsState *server, uint32_t id)
+{
+    return g_object_new(TYPE_STREAM_CHANNEL,
+                        "spice-server", server,
+                        "core-interface", reds_get_core_interface(server),
+                        "channel-type", SPICE_CHANNEL_DISPLAY,
+                        // TODO this id should be after all qxl devices
+                        "id", id,
+                        "migration-flags", 0,
+                        "handle-acks", TRUE, // TODO sure ??
+                        NULL);
+}
+
+static void
+stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStream *stream,
+                       int migration, RedChannelCapabilities *caps)
+{
+    StreamChannel *channel = STREAM_CHANNEL(red_channel);
+    StreamChannelClient *client;
+
+    spice_return_if_fail(stream != NULL);
+
+    client = stream_channel_client_new(channel, red_client, stream, migration, caps);
+    spice_return_if_fail(client != NULL);
+}
+
+static void
+stream_channel_constructed(GObject *object)
+{
+    ClientCbs client_cbs = { NULL, };
+    RedChannel *red_channel = RED_CHANNEL(object);
+    RedsState *reds = red_channel_get_server(red_channel);
+
+    G_OBJECT_CLASS(stream_channel_parent_class)->constructed(object);
+
+    client_cbs.connect = stream_channel_connect;
+    red_channel_register_client_cbs(red_channel, &client_cbs, NULL);
+
+    reds_register_channel(reds, red_channel);
+}
+
+static void
+stream_channel_class_init(StreamChannelClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
+
+    object_class->constructed = stream_channel_constructed;
+
+    channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_DISPLAY, NULL);
+    channel_class->handle_message = red_channel_client_handle_message;
+
+    channel_class->send_item = stream_channel_send_item;
+}
+
+static void
+stream_channel_init(StreamChannel *channel)
+{
+}
diff --git a/server/stream-channel.h b/server/stream-channel.h
new file mode 100644
index 00000000..e50e17e9
--- /dev/null
+++ b/server/stream-channel.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2017 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 STREAM_CHANNEL_H_
+#define STREAM_CHANNEL_H_
+
+#include "red-channel.h"
+
+G_BEGIN_DECLS
+
+/**
+ * This type it's a RedChannel class which implement display
+ * channel with input only by stream.
+ * A pointer to StreamChannel can be converted to a RedChannel.
+ */
+typedef struct StreamChannel StreamChannel;
+typedef struct StreamChannelClass StreamChannelClass;
+
+#define TYPE_STREAM_CHANNEL stream_channel_get_type()
+
+#define STREAM_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_CHANNEL, StreamChannel))
+#define STREAM_CHANNEL_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_CHANNEL, StreamChannelClass))
+#define IS_STREAM_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_STREAM_CHANNEL))
+#define IS_STREAM_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_STREAM_CHANNEL))
+#define STREAM_CHANNEL_GET_CLASS(obj) \
+    (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_CHANNEL, StreamChannelClass))
+
+GType stream_channel_get_type(void) G_GNUC_CONST;
+
+/**
+ * Create StreamChannel.
+ */
+StreamChannel* stream_channel_new(RedsState *server, uint32_t id);
+
+G_END_DECLS
+
+#endif /* STREAM_CHANNEL_H_ */
commit 58e6781d8a49942ea283c995f26dc4bf22eadabf
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Fri Jan 13 18:20:43 2017 +0000

    stream-device: Start parsing new protocol from guest
    
    Parse the data sent from the guest to the streaming device.
    At the moment, the data is simply discarded after it is parsed.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/stream-device.c b/server/stream-device.c
index f3a147b8..58edb3d1 100644
--- a/server/stream-device.c
+++ b/server/stream-device.c
@@ -19,6 +19,8 @@
 #include <config.h>
 #endif
 
+#include <spice/stream-device.h>
+
 #include "char-device.h"
 
 #define TYPE_STREAM_DEVICE stream_device_get_type()
@@ -35,6 +37,9 @@ typedef struct StreamDeviceClass StreamDeviceClass;
 
 struct StreamDevice {
     RedCharDevice parent;
+    StreamDevHeader hdr;
+    uint8_t hdr_pos;
+    bool has_error;
 };
 
 struct StreamDeviceClass {
@@ -46,21 +51,136 @@ static StreamDevice *stream_device_new(SpiceCharDeviceInstance *sin, RedsState *
 
 G_DEFINE_TYPE(StreamDevice, stream_device, RED_TYPE_CHAR_DEVICE)
 
+typedef bool StreamMsgHandler(StreamDevice *dev, SpiceCharDeviceInstance *sin)
+    SPICE_GNUC_WARN_UNUSED_RESULT;
+
+static StreamMsgHandler handle_msg_format, handle_msg_data;
+
+static bool handle_msg_invalid(StreamDevice *dev, SpiceCharDeviceInstance *sin,
+                               const char *error_msg) SPICE_GNUC_WARN_UNUSED_RESULT;
+
 static RedPipeItem *
 stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *sin)
 {
+    StreamDevice *dev = STREAM_DEVICE(self);
     SpiceCharDeviceInterface *sif;
     int n;
+    bool handled = false;
+
+    if (dev->has_error) {
+        return NULL;
+    }
 
     sif = spice_char_device_get_interface(sin);
 
-    do {
-        uint8_t buf[256];
+    /* read header */
+    while (dev->hdr_pos < sizeof(dev->hdr)) {
+        n = sif->read(sin, (uint8_t *) &dev->hdr, sizeof(dev->hdr) - dev->hdr_pos);
+        if (n <= 0) {
+            return NULL;
+        }
+        dev->hdr_pos += n;
+        if (dev->hdr_pos >= sizeof(dev->hdr)) {
+            dev->hdr.type = GUINT16_FROM_LE(dev->hdr.type);
+            dev->hdr.size = GUINT32_FROM_LE(dev->hdr.size);
+        }
+    }
+
+    switch ((StreamMsgType) dev->hdr.type) {
+    case STREAM_TYPE_FORMAT:
+        if (dev->hdr.size != sizeof(StreamMsgFormat)) {
+            handled = handle_msg_invalid(dev, sin, "Wrong size for StreamMsgFormat");
+        } else {
+            handled = handle_msg_format(dev, sin);
+        }
+        break;
+    case STREAM_TYPE_DATA:
+        handled = handle_msg_data(dev, sin);
+        break;
+    case STREAM_TYPE_CAPABILITIES:
+        /* FIXME */
+    default:
+        handled = handle_msg_invalid(dev, sin, "Invalid message type");
+        break;
+    }
+
+    /* current message has been handled, so reset state and get ready to parse
+     * the next message */
+    if (handled) {
+        dev->hdr_pos = 0;
+    }
+
+    return NULL;
+}
+
+static bool
+handle_msg_invalid(StreamDevice *dev, SpiceCharDeviceInstance *sin, const char *error_msg)
+{
+    static const char default_error_msg[] = "Protocol error";
+
+    if (!error_msg) {
+        error_msg = default_error_msg;
+    }
+
+    int msg_size = sizeof(StreamMsgNotifyError) + strlen(error_msg) + 1;
+    int total_size = sizeof(StreamDevHeader) + msg_size;
+
+    RedCharDevice *char_dev = RED_CHAR_DEVICE(dev);
+    RedCharDeviceWriteBuffer *buf =
+        red_char_device_write_buffer_get_server_no_token(char_dev, total_size);
+    buf->buf_used = total_size;
+
+    StreamDevHeader *const hdr = (StreamDevHeader *)buf->buf;
+    hdr->protocol_version = STREAM_DEVICE_PROTOCOL;
+    hdr->padding = 0;
+    hdr->type = GUINT16_TO_LE(STREAM_TYPE_NOTIFY_ERROR);
+    hdr->size = GUINT32_TO_LE(msg_size);
+
+    StreamMsgNotifyError *const error = (StreamMsgNotifyError *)(hdr+1);
+    error->error_code = GUINT32_TO_LE(0);
+    strcpy((char *) error->msg, error_msg);
+
+    red_char_device_write_buffer_add(char_dev, buf);
+
+    dev->has_error = true;
+    return false;
+}
+
+static bool
+handle_msg_format(StreamDevice *dev, SpiceCharDeviceInstance *sin)
+{
+    StreamMsgFormat fmt;
+    SpiceCharDeviceInterface *sif = spice_char_device_get_interface(sin);
+    int n = sif->read(sin, (uint8_t *) &fmt, sizeof(fmt));
+    if (n == 0) {
+        return false;
+    }
+    if (n != sizeof(fmt)) {
+        return handle_msg_invalid(dev, sin, NULL);
+    }
+    fmt.width = GUINT32_FROM_LE(fmt.width);
+    fmt.height = GUINT32_FROM_LE(fmt.height);
+
+    return true;
+}
+
+static bool
+handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin)
+{
+    SpiceCharDeviceInterface *sif = spice_char_device_get_interface(sin);
+    int n;
+    while (1) {
+        uint8_t buf[16 * 1024];
         n = sif->read(sin, buf, sizeof(buf));
+        /* TODO */
         spice_debug("read %d bytes from device", n);
-    } while (n > 0);
+        if (n <= 0) {
+            break;
+        }
+        dev->hdr.size -= n;
+    }
 
-    return NULL;
+    return dev->hdr.size == 0;
 }
 
 static void
commit a0c184e89bcd517c0053fbf74ba3c6e6d331d832
Author: Frediano Ziglio <fziglio at redhat.com>
Date:   Sat Jan 21 11:24:58 2017 +0000

    stream-device: Add device to handle streaming
    
    Add a stub device in guest.
    The aim of this device is to make it possible for the guest to send a
    stream through a DisplayChannel (in the sense of protocol channel).
    This stub allows the guest to send some data and you can see some debug
    lines of data arrived on host logs.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/server/Makefile.am b/server/Makefile.am
index 5d5590af..f08ddf88 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -166,6 +166,7 @@ libserver_la_SOURCES =				\
 	stat.h					\
 	stream.c				\
 	stream.h				\
+	stream-device.c				\
 	sw-canvas.c				\
 	tree.c					\
 	tree.h					\
diff --git a/server/char-device.h b/server/char-device.h
index dccd576d..54a1ef93 100644
--- a/server/char-device.h
+++ b/server/char-device.h
@@ -236,6 +236,7 @@ RedCharDevice *spicevmc_device_connect(RedsState *reds,
                                        uint8_t channel_type);
 void spicevmc_device_disconnect(RedsState *reds,
                                 SpiceCharDeviceInstance *char_device);
+RedCharDevice *stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin);
 
 SpiceCharDeviceInterface *spice_char_device_get_interface(SpiceCharDeviceInstance *instance);
 
diff --git a/server/reds.c b/server/reds.c
index 4f0397ac..49a400b8 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3223,6 +3223,8 @@ static int spice_server_char_device_add_interface(SpiceServer *reds,
     else if (strcmp(char_device->subtype, SUBTYPE_PORT) == 0) {
         if (strcmp(char_device->portname, "org.spice-space.webdav.0") == 0) {
             dev_state = spicevmc_device_connect(reds, char_device, SPICE_CHANNEL_WEBDAV);
+        } else if (strcmp(char_device->portname, "com.redhat.stream.0") == 0) {
+            dev_state = stream_device_connect(reds, char_device);
         } else {
             dev_state = spicevmc_device_connect(reds, char_device, SPICE_CHANNEL_PORT);
         }
diff --git a/server/stream-device.c b/server/stream-device.c
new file mode 100644
index 00000000..f3a147b8
--- /dev/null
+++ b/server/stream-device.c
@@ -0,0 +1,130 @@
+/* spice-server character device to handle a video stream
+
+   Copyright (C) 2017 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 "char-device.h"
+
+#define TYPE_STREAM_DEVICE stream_device_get_type()
+
+#define STREAM_DEVICE(obj) \
+    (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_DEVICE, StreamDevice))
+#define STREAM_DEVICE_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_DEVICE, StreamDeviceClass))
+#define STREAM_DEVICE_GET_CLASS(obj) \
+    (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_DEVICE, StreamDeviceClass))
+
+typedef struct StreamDevice StreamDevice;
+typedef struct StreamDeviceClass StreamDeviceClass;
+
+struct StreamDevice {
+    RedCharDevice parent;
+};
+
+struct StreamDeviceClass {
+    RedCharDeviceClass parent_class;
+};
+
+static GType stream_device_get_type(void) G_GNUC_CONST;
+static StreamDevice *stream_device_new(SpiceCharDeviceInstance *sin, RedsState *reds);
+
+G_DEFINE_TYPE(StreamDevice, stream_device, RED_TYPE_CHAR_DEVICE)
+
+static RedPipeItem *
+stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *sin)
+{
+    SpiceCharDeviceInterface *sif;
+    int n;
+
+    sif = spice_char_device_get_interface(sin);
+
+    do {
+        uint8_t buf[256];
+        n = sif->read(sin, buf, sizeof(buf));
+        spice_debug("read %d bytes from device", n);
+    } while (n > 0);
+
+    return NULL;
+}
+
+static void
+stream_device_send_msg_to_client(RedCharDevice *self, RedPipeItem *msg, RedClient *client)
+{
+}
+
+static void
+stream_device_send_tokens_to_client(RedCharDevice *self, RedClient *client, uint32_t tokens)
+{
+    spice_printerr("Not implemented!");
+}
+
+static void
+stream_device_remove_client(RedCharDevice *self, RedClient *client)
+{
+}
+
+RedCharDevice *
+stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
+{
+    SpiceCharDeviceInterface *sif;
+
+    StreamDevice *dev = stream_device_new(sin, reds);
+
+    sif = spice_char_device_get_interface(sin);
+    if (sif->state) {
+        sif->state(sin, 1);
+    }
+
+    return RED_CHAR_DEVICE(dev);
+}
+
+static void
+stream_device_dispose(GObject *object)
+{
+}
+
+static void
+stream_device_class_init(StreamDeviceClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+    RedCharDeviceClass *char_dev_class = RED_CHAR_DEVICE_CLASS(klass);
+
+    object_class->dispose = stream_device_dispose;
+
+    char_dev_class->read_one_msg_from_device = stream_device_read_msg_from_dev;
+    char_dev_class->send_msg_to_client = stream_device_send_msg_to_client;
+    char_dev_class->send_tokens_to_client = stream_device_send_tokens_to_client;
+    char_dev_class->remove_client = stream_device_remove_client;
+}
+
+static void
+stream_device_init(StreamDevice *self)
+{
+}
+
+static StreamDevice *
+stream_device_new(SpiceCharDeviceInstance *sin, RedsState *reds)
+{
+    return g_object_new(TYPE_STREAM_DEVICE,
+                        "sin", sin,
+                        "spice-server", reds,
+                        "client-tokens-interval", 0ULL,
+                        "self-tokens", ~0ULL,
+                        NULL);
+}


More information about the Spice-commits mailing list