[Spice-devel] [PATCH spice-server v6 17/19] WIP stream-device: handle cursor from device
Frediano Ziglio
fziglio at redhat.com
Thu Oct 12 14:52:13 UTC 2017
TODO: reuse message code buffering
code to handle msg_buf can be reused by different messages
we should limit msg_buf base on type of messages
Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
---
server/stream-device.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 137 insertions(+), 1 deletion(-)
diff --git a/server/stream-device.c b/server/stream-device.c
index f87538d49..6210cb5a5 100644
--- a/server/stream-device.c
+++ b/server/stream-device.c
@@ -23,6 +23,7 @@
#include "char-device.h"
#include "stream-channel.h"
+#include "cursor-channel.h"
#include "reds.h"
#define TYPE_STREAM_DEVICE stream_device_get_type()
@@ -46,6 +47,9 @@ struct StreamDevice {
bool opened;
bool flow_stopped;
StreamChannel *stream_channel;
+ CursorChannel *cursor_channel;
+ void *msg_buf;
+ uint32_t msg_len;
};
struct StreamDeviceClass {
@@ -60,7 +64,7 @@ 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 StreamMsgHandler handle_msg_format, handle_msg_data, handle_msg_cursor_set;
static bool handle_msg_invalid(StreamDevice *dev, SpiceCharDeviceInstance *sin,
const char *error_msg) SPICE_GNUC_WARN_UNUSED_RESULT;
@@ -81,6 +85,11 @@ stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *si
/* read header */
while (dev->hdr_pos < sizeof(dev->hdr)) {
+ if (dev->msg_buf) {
+ g_free(dev->msg_buf);
+ dev->msg_buf = NULL;
+ dev->msg_len = 0;
+ }
n = sif->read(sin, (uint8_t *) &dev->hdr, sizeof(dev->hdr) - dev->hdr_pos);
if (n <= 0) {
return NULL;
@@ -103,6 +112,9 @@ stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *si
case STREAM_TYPE_DATA:
handled = handle_msg_data(dev, sin);
break;
+ case STREAM_TYPE_CURSOR_SET:
+ handled = handle_msg_cursor_set(dev, sin);
+ break;
case STREAM_TYPE_CAPABILITIES:
/* FIXME */
default:
@@ -192,6 +204,106 @@ handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin)
return dev->hdr.size == 0;
}
+/*
+ * Returns number of bits required for a given
+ * cursor type.
+ *
+ * Take into account mask bits.
+ * Returns 0 on error.
+ */
+static unsigned int
+get_cursor_type_bits(unsigned int cursor_type)
+{
+ switch (cursor_type) {
+ case SPICE_CURSOR_TYPE_ALPHA:
+ return 32;
+ case SPICE_CURSOR_TYPE_COLOR24:
+ return 25;
+ case SPICE_CURSOR_TYPE_COLOR32:
+ return 33;
+ default:
+ return 0;
+ }
+}
+
+static RedCursorCmd *
+stream_msg_cursor_set_to_cursor_cmd(const StreamMsgCursorSet *msg, size_t msg_size)
+{
+ RedCursorCmd *cmd = g_new0(RedCursorCmd, 1);
+ cmd->type = QXL_CURSOR_SET;
+ cmd->u.set.position.x = 0; // TODO
+ cmd->u.set.position.y = 0; // TODO
+ cmd->u.set.visible = 1; // TODO
+ SpiceCursor *cursor = &cmd->u.set.shape;
+ cursor->header.unique = 0;
+ cursor->header.type = msg->type;
+ cursor->header.width = GUINT16_FROM_LE(msg->width);
+ cursor->header.height = GUINT16_FROM_LE(msg->height);
+ cursor->header.hot_spot_x = GUINT16_FROM_LE(msg->hot_spot_x);
+ cursor->header.hot_spot_y = GUINT16_FROM_LE(msg->hot_spot_y);
+
+ /* Limit cursor size to prevent DoS */
+ if (cursor->header.width > STREAM_MSG_CURSOR_SET_MAX_WIDTH ||
+ cursor->header.height > STREAM_MSG_CURSOR_SET_MAX_HEIGHT) {
+ g_free(cmd);
+ return NULL;
+ }
+
+ const unsigned int cursor_bits = get_cursor_type_bits(cursor->header.type);
+ if (cursor_bits == 0) {
+ g_free(cmd);
+ return NULL;
+ }
+
+ /* Check that enough data has been sent for the cursor.
+ * Note that these computations can't overflow due to sizes checks
+ * above. */
+ size_t size_required = cursor->header.width * cursor->header.height;
+ size_required = SPICE_ALIGN(size_required * cursor_bits, 8) / 8u;
+ if (msg_size < sizeof(StreamMsgCursorSet) + size_required) {
+ g_free(cmd);
+ return NULL;
+ }
+ cursor->data_size = size_required;
+ cursor->data = spice_memdup(msg->data, size_required);
+ return cmd;
+}
+
+static bool
+handle_msg_cursor_set(StreamDevice *dev, SpiceCharDeviceInstance *sin)
+{
+ const unsigned int max_cursor_set_size =
+ STREAM_MSG_CURSOR_SET_MAX_WIDTH * STREAM_MSG_CURSOR_SET_MAX_HEIGHT * 5;
+
+ SpiceCharDeviceInterface *sif = spice_char_device_get_interface(sin);
+
+ // read part of the message till we get all
+ if (!dev->msg_buf) {
+ if (dev->hdr.size >= max_cursor_set_size || dev->hdr.size < sizeof(StreamMsgCursorSet)) {
+ return handle_msg_invalid(dev, sin, NULL);
+ }
+ dev->msg_buf = g_malloc(dev->hdr.size);
+ dev->msg_len = 0;
+ }
+ int n = sif->read(sin, (uint8_t *) dev->msg_buf + dev->msg_len, dev->hdr.size - dev->msg_len);
+ if (n <= 0) {
+ return false;
+ }
+ dev->msg_len += n;
+ if (dev->msg_len != dev->hdr.size) {
+ return false;
+ }
+
+ // trasform the message to a cursor command and process it
+ RedCursorCmd *cmd = stream_msg_cursor_set_to_cursor_cmd(dev->msg_buf, dev->msg_len);
+ if (!cmd) {
+ return handle_msg_invalid(dev, sin, NULL);
+ }
+ cursor_channel_process_cmd(dev->cursor_channel, cmd);
+
+ return true;
+}
+
static void
stream_device_send_msg_to_client(RedCharDevice *self, RedPipeItem *msg, RedClient *client)
{
@@ -289,6 +401,20 @@ stream_device_dispose(GObject *object)
red_channel_destroy(RED_CHANNEL(dev->stream_channel));
dev->stream_channel = NULL;
}
+ if (dev->cursor_channel) {
+ // close all current connections and drop the reference
+ red_channel_destroy(RED_CHANNEL(dev->cursor_channel));
+ dev->cursor_channel = NULL;
+ }
+}
+
+static void
+stream_device_finalize(GObject *object)
+{
+ StreamDevice *dev = STREAM_DEVICE(object);
+
+ g_free(dev->msg_buf);
+ dev->msg_buf = 0;
}
static void
@@ -299,13 +425,22 @@ allocate_channels(StreamDevice *dev)
}
SpiceServer* reds = red_char_device_get_server(RED_CHAR_DEVICE(dev));
+ SpiceCoreInterfaceInternal* core = reds_get_core_interface(reds);
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);
+ CursorChannel *cursor_channel = cursor_channel_new(reds, id, core);
+ ClientCbs client_cbs = { NULL, };
+ client_cbs.connect = (channel_client_connect_proc) cursor_channel_connect;
+ client_cbs.migrate = cursor_channel_client_migrate;
+ red_channel_register_client_cbs(RED_CHANNEL(cursor_channel), &client_cbs, NULL);
+ reds_register_channel(reds, RED_CHANNEL(cursor_channel));
+
dev->stream_channel = stream_channel;
+ dev->cursor_channel = cursor_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);
@@ -347,6 +482,7 @@ stream_device_class_init(StreamDeviceClass *klass)
RedCharDeviceClass *char_dev_class = RED_CHAR_DEVICE_CLASS(klass);
object_class->dispose = stream_device_dispose;
+ object_class->finalize = stream_device_finalize;
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;
--
2.13.6
More information about the Spice-devel
mailing list