spice package update
Signed-off-by: virtbuilder_pel7x64builder0 <virtbuilder@powerel.org>master
parent
7fe13fb087
commit
2fa0175e33
|
@ -0,0 +1,32 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Fri, 6 Oct 2017 09:38:31 +0100
|
||||
Subject: [spice-server] inputs-channel: Check message size handling migration
|
||||
data
|
||||
|
||||
Prevent possible buffer reading overflow.
|
||||
Note that message pointer must be valid and data are checked
|
||||
value by value so even on overflow you just get an error.
|
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
|
||||
Acked-by: Christophe Fergeau <cfergeau@redhat.com>
|
||||
---
|
||||
server/inputs-channel.c | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
diff --git a/server/inputs-channel.c b/server/inputs-channel.c
|
||||
index 8e17cc724..11a338a26 100644
|
||||
--- a/server/inputs-channel.c
|
||||
+++ b/server/inputs-channel.c
|
||||
@@ -505,6 +505,11 @@ static bool inputs_channel_handle_migrate_data(RedChannelClient *rcc,
|
||||
SpiceMigrateDataHeader *header;
|
||||
SpiceMigrateDataInputs *mig_data;
|
||||
|
||||
+ if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataInputs)) {
|
||||
+ spice_warning("bad message size %u", size);
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+
|
||||
header = (SpiceMigrateDataHeader *)message;
|
||||
mig_data = (SpiceMigrateDataInputs *)(header + 1);
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Fri, 6 Oct 2017 08:57:42 +0100
|
||||
Subject: [spice-server] red-channel: Remove
|
||||
red_channel_init_outgoing_messages_window
|
||||
|
||||
This function does not make much sense anymore.
|
||||
Is called by RedVmcChannel which doesn't use RedChannelClient ACKs
|
||||
so the variable changed are not used.
|
||||
Moreover, at red_vmc_channel_constructed() time, there will be no
|
||||
clients yet, so red_channel_init_outgoing_messages() will be a no-op.
|
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
|
||||
Acked-by: Christophe Fergeau <cfergeau@redhat.com>
|
||||
---
|
||||
server/red-channel.c | 8 --------
|
||||
server/red-channel.h | 3 ---
|
||||
server/spicevmc.c | 2 --
|
||||
3 files changed, 13 deletions(-)
|
||||
|
||||
diff --git a/server/red-channel.c b/server/red-channel.c
|
||||
index b5094829e..8c507f202 100644
|
||||
--- a/server/red-channel.c
|
||||
+++ b/server/red-channel.c
|
||||
@@ -421,14 +421,6 @@ void red_channel_push(RedChannel *channel)
|
||||
g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_push, NULL);
|
||||
}
|
||||
|
||||
-// TODO: this function doesn't make sense because the window should be client (WAN/LAN)
|
||||
-// specific
|
||||
-void red_channel_init_outgoing_messages_window(RedChannel *channel)
|
||||
-{
|
||||
- g_list_foreach(channel->priv->clients,
|
||||
- (GFunc)red_channel_client_init_outgoing_messages_window, NULL);
|
||||
-}
|
||||
-
|
||||
void red_channel_pipes_add(RedChannel *channel, RedPipeItem *item)
|
||||
{
|
||||
RedChannelClient *rcc;
|
||||
diff --git a/server/red-channel.h b/server/red-channel.h
|
||||
index e0fe94fec..281ed0c9e 100644
|
||||
--- a/server/red-channel.h
|
||||
+++ b/server/red-channel.h
|
||||
@@ -157,9 +157,6 @@ void red_channel_destroy(RedChannel *channel);
|
||||
/* return true if all the channel clients support the cap */
|
||||
bool red_channel_test_remote_cap(RedChannel *channel, uint32_t cap);
|
||||
|
||||
-/* should be called when a new channel is ready to send messages */
|
||||
-void red_channel_init_outgoing_messages_window(RedChannel *channel);
|
||||
-
|
||||
// helper to push a new item to all channels
|
||||
typedef RedPipeItem *(*new_pipe_item_t)(RedChannelClient *rcc, void *data, int num);
|
||||
int red_channel_pipes_new_add(RedChannel *channel, new_pipe_item_t creator, void *data);
|
||||
diff --git a/server/spicevmc.c b/server/spicevmc.c
|
||||
index 6b9b96fc8..a1685483d 100644
|
||||
--- a/server/spicevmc.c
|
||||
+++ b/server/spicevmc.c
|
||||
@@ -246,8 +246,6 @@ red_vmc_channel_constructed(GObject *object)
|
||||
red_channel_set_cap(RED_CHANNEL(self), SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4);
|
||||
#endif
|
||||
|
||||
- red_channel_init_outgoing_messages_window(RED_CHANNEL(self));
|
||||
-
|
||||
reds_register_channel(reds, RED_CHANNEL(self));
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Tue, 19 Sep 2017 08:28:02 +0100
|
||||
Subject: [spice-server] reds: Remove leak allocating migration state
|
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
|
||||
Acked-by: Christophe Fergeau <cfergeau@redhat.com>
|
||||
---
|
||||
server/reds.c | 2 +-
|
||||
server/reds.h | 4 ----
|
||||
2 files changed, 1 insertion(+), 5 deletions(-)
|
||||
|
||||
diff --git a/server/reds.c b/server/reds.c
|
||||
index 12c33570a..1b1ab94ea 100644
|
||||
--- a/server/reds.c
|
||||
+++ b/server/reds.c
|
||||
@@ -3378,7 +3378,7 @@ SPICE_GNUC_VISIBLE int spice_server_add_interface(SpiceServer *reds,
|
||||
return -1;
|
||||
}
|
||||
reds->migration_interface = SPICE_CONTAINEROF(sin, SpiceMigrateInstance, base);
|
||||
- reds->migration_interface->st = spice_new0(SpiceMigrateState, 1);
|
||||
+ reds->migration_interface->st = (SpiceMigrateState *)(intptr_t)1; // dummy pointer
|
||||
}
|
||||
|
||||
return 0;
|
||||
diff --git a/server/reds.h b/server/reds.h
|
||||
index 4f5fc28c3..cea002c51 100644
|
||||
--- a/server/reds.h
|
||||
+++ b/server/reds.h
|
||||
@@ -35,10 +35,6 @@ static inline QXLInterface * qxl_get_interface(QXLInstance *qxl)
|
||||
return SPICE_CONTAINEROF(qxl->base.sif, QXLInterface, base);
|
||||
}
|
||||
|
||||
-struct SpiceMigrateState {
|
||||
- int dummy;
|
||||
-};
|
||||
-
|
||||
/* main thread only */
|
||||
void reds_handle_channel_event(RedsState *reds, int event, SpiceChannelEventInfo *info);
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Tue, 19 Sep 2017 08:27:38 +0100
|
||||
Subject: [spice-server] tests: Check leaks registering migration interface
|
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
|
||||
Acked-by: Christophe Fergeau <cfergeau@redhat.com>
|
||||
---
|
||||
server/tests/test-leaks.c | 36 ++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 36 insertions(+)
|
||||
|
||||
diff --git a/server/tests/test-leaks.c b/server/tests/test-leaks.c
|
||||
index 04dcac4f4..7032000aa 100644
|
||||
--- a/server/tests/test-leaks.c
|
||||
+++ b/server/tests/test-leaks.c
|
||||
@@ -143,12 +143,48 @@ static void vmc_leaks(void)
|
||||
basic_event_loop_destroy();
|
||||
}
|
||||
|
||||
+static void migrate_cb(SpiceMigrateInstance *sin)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+static const SpiceMigrateInterface migrate_interface = {
|
||||
+ .base = {
|
||||
+ .type = SPICE_INTERFACE_MIGRATION,
|
||||
+ .description = "migration",
|
||||
+ .major_version = SPICE_INTERFACE_MIGRATION_MAJOR,
|
||||
+ .minor_version = SPICE_INTERFACE_MIGRATION_MINOR,
|
||||
+ },
|
||||
+ .migrate_connect_complete = migrate_cb,
|
||||
+ .migrate_end_complete = migrate_cb,
|
||||
+};
|
||||
+
|
||||
+static void migration_leaks(void)
|
||||
+{
|
||||
+ SpiceCoreInterface *core;
|
||||
+ SpiceServer *server = spice_server_new();
|
||||
+ SpiceMigrateInstance migrate;
|
||||
+
|
||||
+ g_assert_nonnull(server);
|
||||
+
|
||||
+ core = basic_event_loop_init();
|
||||
+ g_assert_nonnull(core);
|
||||
+
|
||||
+ g_assert_cmpint(spice_server_init(server, core), ==, 0);
|
||||
+
|
||||
+ migrate.base.sif = &migrate_interface.base;
|
||||
+ spice_server_add_interface(server, &migrate.base);
|
||||
+
|
||||
+ spice_server_destroy(server);
|
||||
+ basic_event_loop_destroy();
|
||||
+}
|
||||
+
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func("/server/server leaks", server_leaks);
|
||||
g_test_add_func("/server/vmc leaks", vmc_leaks);
|
||||
+ g_test_add_func("/server/migration leaks", migration_leaks);
|
||||
|
||||
return g_test_run();
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Thu, 24 Aug 2017 23:37:25 +0100
|
||||
Subject: [spice-server] Notify client of the creation of new channels
|
||||
dynamically
|
||||
|
||||
This allows the server to add channels after the client is connected.
|
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/main-channel-client.c | 51 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
server/main-channel-client.h | 3 +++
|
||||
server/main-channel.c | 6 ++++++
|
||||
server/main-channel.h | 3 +++
|
||||
server/reds.c | 2 ++
|
||||
5 files changed, 65 insertions(+)
|
||||
|
||||
diff --git a/server/main-channel-client.c b/server/main-channel-client.c
|
||||
index b7b60eddb..61a2830fa 100644
|
||||
--- a/server/main-channel-client.c
|
||||
+++ b/server/main-channel-client.c
|
||||
@@ -62,6 +62,7 @@ struct MainChannelClientPrivate {
|
||||
int mig_wait_prev_try_seamless;
|
||||
int init_sent;
|
||||
int seamless_mig_dst;
|
||||
+ bool initial_channels_list_sent;
|
||||
uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE];
|
||||
};
|
||||
|
||||
@@ -119,6 +120,12 @@ typedef struct RedMultiMediaTimePipeItem {
|
||||
uint32_t time;
|
||||
} RedMultiMediaTimePipeItem;
|
||||
|
||||
+typedef struct RedRegisteredChannelPipeItem {
|
||||
+ RedPipeItem base;
|
||||
+ uint32_t channel_type;
|
||||
+ uint32_t channel_id;
|
||||
+} RedRegisteredChannelPipeItem;
|
||||
+
|
||||
#define ZERO_BUF_SIZE 4096
|
||||
|
||||
static const uint8_t zero_page[ZERO_BUF_SIZE] = {0};
|
||||
@@ -446,6 +453,20 @@ RedPipeItem *main_multi_media_time_item_new(uint32_t mm_time)
|
||||
return &item->base;
|
||||
}
|
||||
|
||||
+RedPipeItem *registered_channel_item_new(RedChannel *channel)
|
||||
+{
|
||||
+ RedRegisteredChannelPipeItem *item;
|
||||
+
|
||||
+ item = g_new0(RedRegisteredChannelPipeItem, 1);
|
||||
+ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL);
|
||||
+
|
||||
+ uint32_t type, id;
|
||||
+ g_object_get(channel, "channel-type", &type, "id", &id, NULL);
|
||||
+ item->channel_type = type;
|
||||
+ item->channel_id = id;
|
||||
+ return &item->base;
|
||||
+}
|
||||
+
|
||||
void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
|
||||
int success,
|
||||
int seamless)
|
||||
@@ -927,6 +948,25 @@ static void main_channel_marshall_agent_connected(SpiceMarshaller *m,
|
||||
spice_marshall_msg_main_agent_connected_tokens(m, &connected);
|
||||
}
|
||||
|
||||
+static void main_channel_marshall_registered_channel(RedChannelClient *rcc,
|
||||
+ SpiceMarshaller *m,
|
||||
+ RedRegisteredChannelPipeItem *item)
|
||||
+{
|
||||
+ struct {
|
||||
+ SpiceMsgChannels info;
|
||||
+ SpiceChannelId ids[1];
|
||||
+ } channels_info_buffer;
|
||||
+ SpiceMsgChannels* channels_info = &channels_info_buffer.info;
|
||||
+
|
||||
+ red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_CHANNELS_LIST);
|
||||
+
|
||||
+ channels_info->channels[0].type = item->channel_type;
|
||||
+ channels_info->channels[0].id = item->channel_id;
|
||||
+ channels_info->num_of_channels = 1;
|
||||
+
|
||||
+ spice_marshall_msg_main_channels_list(m, channels_info);
|
||||
+}
|
||||
+
|
||||
void main_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *base)
|
||||
{
|
||||
MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc);
|
||||
@@ -947,6 +987,7 @@ void main_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *base)
|
||||
switch (base->type) {
|
||||
case RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST:
|
||||
main_channel_marshall_channels(rcc, m, base);
|
||||
+ mcc->priv->initial_channels_list_sent = true;
|
||||
break;
|
||||
case RED_PIPE_ITEM_TYPE_MAIN_PING:
|
||||
main_channel_marshall_ping(rcc, m,
|
||||
@@ -1003,6 +1044,16 @@ void main_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *base)
|
||||
case RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS:
|
||||
main_channel_marshall_agent_connected(m, rcc, base);
|
||||
break;
|
||||
+ case RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL:
|
||||
+ /* The spice protocol requires that the server receive a ATTACH_CHANNELS
|
||||
+ * message from the client before sending any CHANNEL_LIST message. If
|
||||
+ * we've already sent our initial CHANNELS_LIST message, then it should be
|
||||
+ * safe to send new ones for newly-registered channels. */
|
||||
+ if (mcc->priv->initial_channels_list_sent) {
|
||||
+ main_channel_marshall_registered_channel(rcc, m,
|
||||
+ SPICE_UPCAST(RedRegisteredChannelPipeItem, base));
|
||||
+ }
|
||||
+ break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
diff --git a/server/main-channel-client.h b/server/main-channel-client.h
|
||||
index 26b7e20b8..2cf2e3424 100644
|
||||
--- a/server/main-channel-client.h
|
||||
+++ b/server/main-channel-client.h
|
||||
@@ -122,12 +122,15 @@ enum {
|
||||
RED_PIPE_ITEM_TYPE_MAIN_NAME,
|
||||
RED_PIPE_ITEM_TYPE_MAIN_UUID,
|
||||
RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
|
||||
+ RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL,
|
||||
};
|
||||
|
||||
RedPipeItem *main_mouse_mode_item_new(SpiceMouseMode current_mode, int is_client_mouse_allowed);
|
||||
|
||||
RedPipeItem *main_multi_media_time_item_new(uint32_t mm_time);
|
||||
|
||||
+RedPipeItem *registered_channel_item_new(RedChannel *channel);
|
||||
+
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* MAIN_CHANNEL_CLIENT_H_ */
|
||||
diff --git a/server/main-channel.c b/server/main-channel.c
|
||||
index eca857f6b..4dc130e40 100644
|
||||
--- a/server/main-channel.c
|
||||
+++ b/server/main-channel.c
|
||||
@@ -149,6 +149,12 @@ static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice
|
||||
main_channel->mig_target.sport = mig_target->sport;
|
||||
}
|
||||
|
||||
+void
|
||||
+main_channel_registered_new_channel(MainChannel *main_chan, RedChannel *channel)
|
||||
+{
|
||||
+ red_channel_pipes_add(RED_CHANNEL(main_chan), registered_channel_item_new(channel));
|
||||
+}
|
||||
+
|
||||
void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target)
|
||||
{
|
||||
main_channel_fill_mig_target(main_chan, mig_target);
|
||||
diff --git a/server/main-channel.h b/server/main-channel.h
|
||||
index eb3bcec3a..0cb5be728 100644
|
||||
--- a/server/main-channel.h
|
||||
+++ b/server/main-channel.h
|
||||
@@ -66,6 +66,9 @@ void main_channel_push_mouse_mode(MainChannel *main_chan, SpiceMouseMode current
|
||||
void main_channel_push_agent_connected(MainChannel *main_chan);
|
||||
void main_channel_push_agent_disconnected(MainChannel *main_chan);
|
||||
void main_channel_push_multi_media_time(MainChannel *main_chan, uint32_t time);
|
||||
+/* tell MainChannel we have a new channel ready */
|
||||
+void main_channel_registered_new_channel(MainChannel *main_chan,
|
||||
+ RedChannel *channel);
|
||||
|
||||
int main_channel_is_connected(MainChannel *main_chan);
|
||||
|
||||
diff --git a/server/reds.c b/server/reds.c
|
||||
index 1b1ab94ea..99b1fd76b 100644
|
||||
--- a/server/reds.c
|
||||
+++ b/server/reds.c
|
||||
@@ -387,6 +387,8 @@ void reds_register_channel(RedsState *reds, RedChannel *channel)
|
||||
{
|
||||
spice_assert(reds);
|
||||
reds->channels = g_list_prepend(reds->channels, channel);
|
||||
+ // create new channel in the client if possible
|
||||
+ main_channel_registered_new_channel(reds->main_channel, channel);
|
||||
}
|
||||
|
||||
void reds_unregister_channel(RedsState *reds, RedChannel *channel)
|
|
@ -0,0 +1,194 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Sat, 21 Jan 2017 11:24:58 +0000
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/Makefile.am | 1 +
|
||||
server/char-device.h | 1 +
|
||||
server/reds.c | 2 +
|
||||
server/stream-device.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
4 files changed, 134 insertions(+)
|
||||
create mode 100644 server/stream-device.c
|
||||
|
||||
diff --git a/server/Makefile.am b/server/Makefile.am
|
||||
index 5d5590af9..f08ddf883 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 dccd576da..54a1ef939 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 99b1fd76b..b24f61ab2 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 000000000..f3a147b80
|
||||
--- /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);
|
||||
+}
|
|
@ -0,0 +1,178 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Fri, 13 Jan 2017 18:20:43 +0000
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/stream-device.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++--
|
||||
1 file changed, 124 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/server/stream-device.c b/server/stream-device.c
|
||||
index f3a147b80..58edb3d11 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
|
|
@ -0,0 +1,271 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Sat, 21 Jan 2017 15:29:18 +0000
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/Makefile.am | 2 +
|
||||
server/stream-channel.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
server/stream-channel.h | 53 +++++++++++++++
|
||||
3 files changed, 230 insertions(+)
|
||||
create mode 100644 server/stream-channel.c
|
||||
create mode 100644 server/stream-channel.h
|
||||
|
||||
diff --git a/server/Makefile.am b/server/Makefile.am
|
||||
index f08ddf883..e2e3ce861 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 000000000..fd735f562
|
||||
--- /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 000000000..e50e17e9e
|
||||
--- /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_ */
|
|
@ -0,0 +1,88 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Tue, 14 Mar 2017 12:12:22 +0000
|
||||
Subject: [spice-server] stream-channel: Start implementing DisplayChannel
|
||||
properly
|
||||
|
||||
Handle messages from clients.
|
||||
Send some messages to clients.
|
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/stream-channel.c | 41 ++++++++++++++++++++++++++++++++++++++++-
|
||||
1 file changed, 40 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c
|
||||
index fd735f562..df6936513 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;
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Sat, 21 Jan 2017 14:02:29 +0000
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/stream-channel.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++--
|
||||
server/stream-device.c | 14 +++++++++++++
|
||||
2 files changed, 64 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c
|
||||
index df6936513..baf3d58a6 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 58edb3d11..0c9173ae0 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
|
|
@ -0,0 +1,231 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Sat, 21 Jan 2017 10:47:40 +0000
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/stream-channel.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
server/stream-channel.h | 8 ++++
|
||||
server/stream-device.c | 4 +-
|
||||
3 files changed, 113 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c
|
||||
index baf3d58a6..8931d8794 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 e50e17e9e..156a75d31 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 0c9173ae0..6e78b1a99 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Sat, 21 Jan 2017 19:03:11 +0000
|
||||
Subject: [spice-server] stream-channel: Allows not fixed size
|
||||
|
||||
Remove the fixed size stream and support any display size.
|
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/stream-channel.c | 31 +++++++++++++++++++++++++------
|
||||
1 file changed, 25 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c
|
||||
index 8931d8794..59f566f50 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;
|
|
@ -0,0 +1,67 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Sat, 21 Jan 2017 19:08:12 +0000
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/stream-channel.c | 12 ++++++++++++
|
||||
server/stream-channel.h | 6 ++++++
|
||||
2 files changed, 18 insertions(+)
|
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c
|
||||
index 59f566f50..be6f7c7d1 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 156a75d31..ba098df49 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_ */
|
|
@ -0,0 +1,131 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Sat, 21 Jan 2017 19:03:19 +0000
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/stream-channel.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 81 insertions(+)
|
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c
|
||||
index be6f7c7d1..2ad9ebae3 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);
|
|
@ -0,0 +1,64 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Sat, 21 Jan 2017 09:01:18 +0000
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/stream-channel.c | 16 ++++++++++++----
|
||||
1 file changed, 12 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c
|
||||
index 2ad9ebae3..e89563b82 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
|
|
@ -0,0 +1,67 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Tue, 24 Jan 2017 11:36:27 +0000
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/char-device.c | 2 --
|
||||
server/reds.c | 6 +++++-
|
||||
2 files changed, 5 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/server/char-device.c b/server/char-device.c
|
||||
index 658f9f364..f8a098bd8 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 b24f61ab2..401d242fb 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) {
|
|
@ -0,0 +1,166 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Wed, 25 Jan 2017 22:42:00 +0000
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/stream-channel.c | 34 ++++++++++++++++++++++++++++++++++
|
||||
server/stream-channel.h | 7 ++++++-
|
||||
server/stream-device.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 89 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c
|
||||
index e89563b82..51b8badf9 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 ba098df49..bd075a951 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 6e78b1a99..9e401f8ed 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
|
|
@ -0,0 +1,89 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Fri, 25 Aug 2017 00:02:54 +0100
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/stream-device.c | 38 ++++++++++++++++++++++++++++++++------
|
||||
1 file changed, 32 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/server/stream-device.c b/server/stream-device.c
|
||||
index 9e401f8ed..ae108788b 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
|
|
@ -0,0 +1,206 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Wed, 19 Apr 2017 16:24:54 +0100
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/stream-channel.c | 41 ++++++++++++++++++++++++++++++++++++++++-
|
||||
server/stream-channel.h | 10 ++++++++++
|
||||
server/stream-device.c | 35 ++++++++++++++++++++++++++++++++++-
|
||||
3 files changed, 84 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c
|
||||
index 51b8badf9..ec4bf021d 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 bd075a951..f961d7157 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 ae108788b..f87538d49 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);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Frediano Ziglio <fziglio@redhat.com>
|
||||
Date: Wed, 7 Jun 2017 13:13:21 +0100
|
||||
Subject: [spice-server] 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@redhat.com>
|
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
|
||||
---
|
||||
server/stream-channel.c | 19 +++++++++++++++++++
|
||||
1 file changed, 19 insertions(+)
|
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c
|
||||
index ec4bf021d..7e15dd363 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
|
237
SPECS/spice.spec
237
SPECS/spice.spec
|
@ -1,27 +1,50 @@
|
|||
Name: spice
|
||||
Version: 0.14.0
|
||||
Release: 1%{?dist}
|
||||
Release: 2%{?dist}
|
||||
Summary: Implements the SPICE protocol
|
||||
Group: User Interface/Desktops
|
||||
License: LGPLv2+
|
||||
URL: http://www.spice-space.org/
|
||||
Source0: http://www.spice-space.org/download/releases/%{name}-%{version}.tar.bz2
|
||||
Source1: http://www.spice-space.org/download/releases/%{name}-%{version}.tar.bz2.sign
|
||||
Source2: cfergeau-29AC6C82.keyring
|
||||
Patch1: 0001-inputs-channel-Check-message-size-handling-migration.patch
|
||||
Patch2: 0002-red-channel-Remove-red_channel_init_outgoing_message.patch
|
||||
Patch3: 0003-reds-Remove-leak-allocating-migration-state.patch
|
||||
Patch4: 0004-tests-Check-leaks-registering-migration-interface.patch
|
||||
Patch5: 0005-Notify-client-of-the-creation-of-new-channels-dynami.patch
|
||||
Patch6: 0006-stream-device-Add-device-to-handle-streaming.patch
|
||||
Patch7: 0007-stream-device-Start-parsing-new-protocol-from-guest.patch
|
||||
Patch8: 0008-stream-channel-Write-a-base-channel-to-implement-the.patch
|
||||
Patch9: 0009-stream-channel-Start-implementing-DisplayChannel-pro.patch
|
||||
Patch10: 0010-stream-device-Create-channel-for-stream-device.patch
|
||||
Patch11: 0011-stream-device-Handle-streaming-data-from-device-to-c.patch
|
||||
Patch12: 0012-stream-channel-Allows-not-fixed-size.patch
|
||||
Patch13: 0013-stream-channel-Allows-to-register-callback-to-get-ne.patch
|
||||
Patch14: 0014-stream-channel-Support-client-connection-disconnecti.patch
|
||||
Patch15: 0015-stream-channel-Do-not-show-an-empty-blank-screen-on-.patch
|
||||
Patch16: 0016-char-device-Do-not-stop-and-clear-interface-on-reset.patch
|
||||
Patch17: 0017-stream-device-Start-supporting-resetting-device-when.patch
|
||||
Patch18: 0018-stream-device-Create-channel-when-needed.patch
|
||||
Patch19: 0019-stream-device-Limit-sending-queue-from-guest-to-serv.patch
|
||||
Patch20: 0020-stream-channel-Activate-streaming-report-from-client.patch
|
||||
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=613529
|
||||
%if 0%{?rhel}
|
||||
ExclusiveArch: x86_64
|
||||
%else
|
||||
ExclusiveArch: i686 x86_64 armv6l armv7l armv7hl
|
||||
%endif
|
||||
|
||||
BuildRequires: pkgconfig
|
||||
BuildRequires: glib2-devel >= 2.22
|
||||
BuildRequires: spice-protocol >= 0.12.3
|
||||
BuildRequires: spice-protocol >= 0.12.10
|
||||
BuildRequires: celt051-devel
|
||||
BuildRequires: opus-devel
|
||||
BuildRequires: pixman-devel openssl-devel libjpeg-devel
|
||||
BuildRequires: pixman-devel alsa-lib-devel openssl-devel libjpeg-turbo-devel
|
||||
BuildRequires: libcacard-devel cyrus-sasl-devel
|
||||
BuildRequires: lz4-devel
|
||||
BuildRequires: gstreamer1-devel gstreamer1-plugins-base-devel
|
||||
BuildRequires: orc-devel
|
||||
BuildRequires: pyparsing
|
||||
BuildRequires: python-six
|
||||
BuildRequires: gnupg2
|
||||
BuildRequires: git-core
|
||||
BuildRequires: opus-devel
|
||||
BuildRequires: git
|
||||
BuildRequires: autoconf automake libtool
|
||||
|
||||
%description
|
||||
The Simple Protocol for Independent Computing Environments (SPICE) is
|
||||
|
@ -52,6 +75,7 @@ Summary: Header files, libraries and development documentation for spice-
|
|||
Group: Development/Libraries
|
||||
Requires: %{name}-server%{?_isa} = %{version}-%{release}
|
||||
Requires: pkgconfig
|
||||
Requires: spice-protocol >= 0.12.3
|
||||
|
||||
%description server-devel
|
||||
This package contains the header files, static libraries and development
|
||||
|
@ -60,13 +84,12 @@ using spice-server, you will need to install spice-server-devel.
|
|||
|
||||
|
||||
%prep
|
||||
gpgv2 --quiet --keyring %{SOURCE2} %{SOURCE1} %{SOURCE0}
|
||||
%autosetup -S git_am
|
||||
|
||||
|
||||
%build
|
||||
%define configure_client --disable-client
|
||||
%configure --enable-smartcard --disable-client --enable-lz4 --enable-gstreamer=1.0
|
||||
autoreconf -fi
|
||||
%configure --enable-smartcard --disable-client
|
||||
make %{?_smp_mflags} WARN_CFLAGS='' V=1
|
||||
|
||||
|
||||
|
@ -82,9 +105,7 @@ mkdir -p %{buildroot}%{_libexecdir}
|
|||
|
||||
|
||||
%files server
|
||||
%{!?_licensedir:%global license %%doc}
|
||||
%license COPYING
|
||||
%doc README NEWS
|
||||
%doc COPYING README NEWS
|
||||
%{_libdir}/libspice-server.so.1*
|
||||
|
||||
%files server-devel
|
||||
|
@ -94,87 +115,165 @@ mkdir -p %{buildroot}%{_libexecdir}
|
|||
|
||||
|
||||
%changelog
|
||||
* Thu Oct 12 2017 Christophe Fergeau <cfergeau@redhat.com> - 0.14.0-2
|
||||
- Add streaming patches for use with spice-streaming-agent
|
||||
Related: rhbz#1478356
|
||||
|
||||
* Wed Oct 11 2017 Christophe Fergeau <cfergeau@redhat.com> - 0.14.0-1
|
||||
- Update to new stable release
|
||||
- Rebase to 0.14.0 release
|
||||
Resolves: rhbz#1472948
|
||||
|
||||
* Tue Sep 26 2017 Christophe Fergeau <christophe@redhat.com> - 0.13.91-1
|
||||
- Update to latest upstream release
|
||||
|
||||
* Thu Aug 24 2017 Christophe Fergeau <cfergeau@redhat.com> - 0.13.90-3
|
||||
- Add missing (new) BuildRequires, remove obsolete one
|
||||
|
||||
* Thu Aug 03 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.13.90-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
|
||||
* Fri Sep 22 2017 Christophe Fergeau <cfergeau@redhat.com> 0.13.90-2
|
||||
- Add lz4-devel BuildRequires
|
||||
Resolves: rhbz#1460191
|
||||
|
||||
* Wed Jul 26 2017 Christophe Fergeau <cfergeau@redhat.com> 0.13.90-1
|
||||
- Update to latest upstream release (0.13.90)
|
||||
- Rebase to latest upstream release
|
||||
Resolves: rhbz#1472948
|
||||
|
||||
* Mon Feb 06 2017 Christophe Fergeau <cfergeau@redhat.com> 0.13.3-2
|
||||
- Add upstream patches fixing CVE-2016-9577 and CVE-2016-9578
|
||||
* Fri Jul 14 2017 Jonathon Jongsma <jjongsma@redhat.com> - 0.12.8-4
|
||||
- build with opus support
|
||||
Resolves: rhbz#1456832
|
||||
|
||||
* Mon Nov 21 2016 Christophe Fergeau <cfergeau@redhat.com> 0.13.3-1
|
||||
- Update to spice 0.13.3
|
||||
* Fri Jun 30 2017 Christophe Fergeau <cfergeau@redhat.com> 0.12.8-3
|
||||
- Prevent potential buffer/integer overflows with invalid MonitorsConfig messages
|
||||
sent from an authenticated client
|
||||
Resolves: CVE-2017-7506
|
||||
|
||||
* Fri Aug 05 2016 Christophe Fergeau <cfergeau@redhat.com> - 0.13.2-1
|
||||
- Update to spice 0.13.2
|
||||
* Tue Apr 25 2017 Christophe Fergeau <cfergeau@redhat.com> 0.12.8-2
|
||||
- Drop clients immediatly if the magic they send is wrong
|
||||
Resolves: rhbz#1416692
|
||||
|
||||
* Tue Jun 14 2016 Peter Robinson <pbrobinson@fedoraproject.org> 0.13.1-2
|
||||
- Use %%license
|
||||
- Build on aarch64
|
||||
* Mon Jan 16 2017 Christophe Fergeau <cfergeau@redhat.com> 0.12.8-1
|
||||
- Rebase to spice-server 0.12.8
|
||||
Resolves: rhbz#1388947
|
||||
Resolves: rhbz#1377551
|
||||
Resolves: rhbz#1283202
|
||||
* Fri Dec 09 2016 Frediano Ziglio <fziglio@redhat.com> - 0.12.4-20
|
||||
- Fix buffer overflow in main_channel_alloc_msg_rcv_buf when reading large
|
||||
messages.
|
||||
Resolves: CVE-2016-9577
|
||||
- Fix remote DoS via crafted message.
|
||||
Resolves: CVE-2016-9578
|
||||
* Fri Sep 09 2016 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-19
|
||||
- Ensure SPICE_MIGRATE_COMPLETED is sent in all cases when it's needed.
|
||||
Resolves: rhbz#1352836
|
||||
* Fri Jul 01 2016 Christophe Fergeau <cfergeau@redhat.com> - 0.12.4-18
|
||||
- Fix crash when connecting to VM using smartcard passthrough
|
||||
Resolves: rhbz#1340899
|
||||
- Fix hang after unredirecting a USB device
|
||||
Resolves: rhbz#1338752
|
||||
- Backport spice_qxl_set_max_monitors()
|
||||
Resolves: rhbz#1283202
|
||||
* Wed Apr 27 2016 Christophe Fergeau <cfergeau@redhat.com> - 0.12.4-17
|
||||
- Fix crash when the client sends a wrong header (for example when using spice-html5)
|
||||
Resolves: rhbz#1281442
|
||||
- Fix crash when guest provides wrong address
|
||||
Resolves: rhbz#1264356
|
||||
- Fix thread-safety issue causing a crash when playing a Youtube video spanning
|
||||
multiple monitors
|
||||
Resolves: rhbz#1253375
|
||||
- Add patches reducing QEMU wake-ups
|
||||
Related: rhbz#912763, rhbz#1186146
|
||||
- Fix use-after-free after resetting a VM
|
||||
Resolves: rhbz#1281455
|
||||
- Send KeepAlive probes every 10 minutes
|
||||
Resolves: rhbz#1298590
|
||||
- Add client to guest volume synchronization
|
||||
Resolves: rhbz#1264107
|
||||
|
||||
* Wed Apr 13 2016 Christophe Fergeau <cfergeau@redhat.com> 0.13.1-1
|
||||
- Update to 0.13.1 release. This is a development release, but by the
|
||||
time Fedora 25 gets released, a stable 0.14.0 should be released.
|
||||
* Mon Apr 25 2016 Christophe Fergeau <cfergeau@redhat.com> - 0.12.4-16
|
||||
- Use autosetup
|
||||
Related: CVE-2016-0749
|
||||
- Fix heap-based memory corruption within smartcard handling
|
||||
Resolves: CVE-2016-0749
|
||||
- Fix host memory access from guest with invalid primary surface parameters
|
||||
Resolves: CVE-2016-2150
|
||||
|
||||
* Fri Feb 05 2016 Fedora Release Engineering <releng@fedoraproject.org> - 0.12.6-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
|
||||
* Wed Sep 23 2015 Frediano Ziglio <fziglio@redhat.com> 0.12.4-15
|
||||
- CVE-2015-5260 CVE-2015-5261 fixed various security flaws
|
||||
Resolves: rhbz#1267134
|
||||
|
||||
* Tue Oct 06 2015 Christophe Fergeau <cfergeau@redhat.com> 0.12.6-1
|
||||
- Update to new 0.12.6 upstream release
|
||||
* Thu Sep 10 2015 Frediano Ziglio <fziglio@redhat.com> 0.12.4-14
|
||||
- Validate surface_id
|
||||
Resolves: rhbz#1260971
|
||||
|
||||
* Wed Jul 29 2015 Christophe Fergeau <cfergeau@redhat.com> 0.12.5-9
|
||||
- Drop patch added in previous build which is no longer needed with
|
||||
spice-protocol 0.12.9 (and actually is actually breaking QEMU compilation
|
||||
without an additional patch)
|
||||
* Tue Jul 21 2015 Frediano Ziglio <fziglio@redhat.com> 0.12.4-13
|
||||
- Clean stale statistics file before creating a new one
|
||||
Resolves: rhbz#1177326
|
||||
|
||||
* Fri Jul 03 2015 Christophe Fergeau <cfergeau@redhat.com> 0.12.5-8
|
||||
- Add upstream patch avoiding a regression in spice-protocol 0.12.8 which
|
||||
breaks SPICE support in QEMU
|
||||
* Fri Jul 10 2015 Fabiano Fidêncio <fidencio@redhat.com> 0.12.4-12
|
||||
- Fix a backport issue on Patch0040.
|
||||
Related: rhbz#1071176
|
||||
Resolves: rhbz#1241860
|
||||
|
||||
* Thu Jul 02 2015 Christophe Fergeau <cfergeau@redhat.com> 0.12.5-7
|
||||
- Fix migration race condition which causes a crash when triggered
|
||||
Resolves: rhbz#1238212
|
||||
* Thu Jul 09 2015 Fabiano Fidêncio <fidencio@redhat.com> 0.12.4-11
|
||||
- Don't assert on invalid client message
|
||||
Resolves: rhbz#1227410
|
||||
- Don't truncate large 'now' values in _spice_timer_set
|
||||
Resolves: rhbz#1227408
|
||||
- Avoid race conditions reading monitor configs from guest
|
||||
Resolves: rhbz#1239128
|
||||
- Lock the pixmap image cache for the entire fill_bits call
|
||||
Resolves: rhbz#1235443
|
||||
|
||||
* Fri Jun 19 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.12.5-6
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
|
||||
* Wed Jul 08 2015 Fabiano Fidêncio <fidencio@redhat.com> 0.12.4-10
|
||||
- Fix qemu segmentation fault (core dumped) when boot KVM guest with
|
||||
spice in FIPS enabled mode.
|
||||
Resolves: rhbz#1071176
|
||||
|
||||
* Mon Aug 25 2014 Christophe Fergeau <cfergeau@redhat.com> 0.12.5-5
|
||||
- Fix advertised sound playback/recording rates in public headers
|
||||
Resolves: rhbz#1129961 (QEMU would need a rebuild though)
|
||||
* Mon Jan 05 2015 Marc-Andre Lureau <marcandre.lureau@redhat.com> 0.12.4-9
|
||||
- Allow recent TLS/SSL methods, block SSLv2/SSLv3. Resolves: rhbz#1175540
|
||||
|
||||
* Mon Aug 18 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.12.5-4
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
|
||||
* Tue Oct 21 2014 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-8
|
||||
- Fix defects reported by Coverity
|
||||
Resolves: rhbz#885717
|
||||
- Validate surface bounding box sent from QXL driver
|
||||
Resolves: rhbz#1052856
|
||||
- Fix assertion sometimes happening during migration while a client is
|
||||
connected
|
||||
Resolves: rhbz#1035184
|
||||
- Fix crash when restarting VM with old client
|
||||
Resolves: rhbz#1145919
|
||||
|
||||
* Sun Jun 08 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.12.5-3
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
|
||||
* Thu Sep 18 2014 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-7
|
||||
- Fix assert in mjpeg_encoder_adjust_params_to_bit_rate()
|
||||
Resolves: rhbz#1086823
|
||||
- Fix "Spice-ERROR **: reds.c:1464:reds_send_link_ack: assertion
|
||||
`link->link_mess->channel_type == SPICE_CHANNEL_MAIN' failed" assertion
|
||||
Resolves: rhbz#1058625
|
||||
- Lower a monitor-config warning to debug level
|
||||
Resolves: rhbz#1119220
|
||||
- mjpeg: Don't warn on unsupported image formats
|
||||
Resolves: rhbz#1070028
|
||||
|
||||
* Mon May 19 2014 Christophe Fergeau <cfergeau@redhat.com> 0.12.5-2
|
||||
- Add missing BuildRequires in order to enable Opus support
|
||||
* Thu Aug 07 2014 Marc-Andre Lureau <marcandre.lureau@redhat.com> 0.12.4-6
|
||||
- Fix invalid surface clearing
|
||||
Resolves: rhbz#1029646
|
||||
|
||||
* Mon May 19 2014 Christophe Fergeau <cfergeau@redhat.com> 0.12.5-1
|
||||
- Update to new 0.12.5 release
|
||||
* Wed Jan 29 2014 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-5
|
||||
- Fix qemu crash during migration with reboot
|
||||
Resolves: rhbz#1016795
|
||||
- Monitor whether the client is alive
|
||||
Resolves: rhbz#1016790
|
||||
|
||||
* Wed Oct 30 2013 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-3
|
||||
- Add patch fixing CVE-2013-4282
|
||||
* Tue Oct 15 2013 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-3
|
||||
- Fix spice-server crash when client sends a password which is too long
|
||||
Resolves: CVE-2013-4282
|
||||
|
||||
* Fri Sep 13 2013 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-2
|
||||
- Add upstream patch fixing rhbz#995041
|
||||
|
||||
* Fri Aug 2 2013 Hans de Goede <hdegoede@redhat.com> - 0.12.4-1
|
||||
- New upstream bug-fix release 0.12.4
|
||||
- Add patches from upstream git to fix sound-channel-free crash (rhbz#986407)
|
||||
- Add Obsoletes for dropped spice-client sub-package
|
||||
|
||||
* Mon Jul 22 2013 Yonit Halperin <yhalperi@redhat.com> 0.12.4-1
|
||||
- New upstream release 0.12.4
|
||||
- Require libjpeg-turbo-devel instead of libjpeg-devel
|
||||
- Remove "BuildRequires: spice-protocol" from spice-server
|
||||
- Add "Requires: spice-protocol" to spice-server-devel.
|
||||
|
||||
* Thu May 23 2013 Christophe Fergeau <cfergeau@redhat.com> 0.12.3-2
|
||||
- Stop building spicec, it's obsolete and superseded by remote-viewer
|
||||
(part of virt-viewer)
|
||||
|
|
Loading…
Reference in New Issue