[Spice-commits] 13 commits - server/inputs_channel.c server/inputs_channel.h server/Makefile.am server/red_channel.c server/red_channel.h server/reds.c server/reds.h

Alon Levy alon at kemper.freedesktop.org
Tue Dec 7 11:32:48 PST 2010


 server/Makefile.am      |    1 
 server/inputs_channel.c |  553 ++++++++++++++++++++++++++++++++++++++++++++
 server/inputs_channel.h |   39 +++
 server/red_channel.c    |   90 ++++++-
 server/red_channel.h    |   32 ++
 server/reds.c           |  598 +++---------------------------------------------
 server/reds.h           |   20 -
 7 files changed, 768 insertions(+), 565 deletions(-)

New commits:
commit 96da0a4819bd72dace93aa1fde3cd3152fed018e
Author: Alon Levy <alevy at redhat.com>
Date:   Tue Dec 7 12:56:30 2010 +0200

    server/inputs: return NULL on alloc_buf request for too large message

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index de62bfe..e53a634 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -157,6 +157,10 @@ static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannel *channel, SpiceDataH
 {
     InputsChannel *inputs_channel = SPICE_CONTAINEROF(channel, InputsChannel, base);
 
+    if (msg_header->size > RECEIVE_BUF_SIZE) {
+        red_printf("error: too large incoming message");
+        return NULL;
+    }
     return inputs_channel->recv_buf;
 }
 
commit 380b5e9dd70edc79d13fe07210fc7ff2c004e842
Author: Alon Levy <alevy at redhat.com>
Date:   Tue Dec 7 12:57:06 2010 +0200

    server/red_channel: error channel if alloc_msg_buf fails (returns NULL)

diff --git a/server/red_channel.c b/server/red_channel.c
index cab56a0..3c1aede 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -92,6 +92,11 @@ static void red_peer_handle_incoming(RedsStreamContext *peer, IncomingHandler *h
         if (handler->msg_pos < handler->header.size) {
             if (!handler->msg) {
                 handler->msg = handler->alloc_msg_buf(handler->opaque, &handler->header);
+                if (handler->msg == NULL) {
+                    red_printf("ERROR: channel refused to allocate buffer.");
+                    handler->on_error(handler->opaque);
+                    return;
+                }
             }
 
             bytes_read = red_peer_receive(peer,
commit ec0b90db7fc4e0e27130d0b1bec662ffd7b2ce56
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Nov 17 23:04:02 2010 +0200

    server/inputs_channel: better protection from channel disconnection on keyboard modifiers callback

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index f5f1d73..de62bfe 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -511,7 +511,7 @@ void inputs_send_keyboard_modifiers(uint8_t modifiers)
     InputsPipeItem *pipe_item;
     SpiceMarshaller *m;
 
-    if (!g_inputs_channel) {
+    if (!g_inputs_channel || !red_channel_is_connected(&g_inputs_channel->base)) {
         return;
     }
     pipe_item = inputs_pipe_item_new(g_inputs_channel, PIPE_ITEM_KEY_MODIFIERS);
commit 1540a64030cc82556e6a98230656b17c2ccf4dae
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Nov 7 12:23:59 2010 +0200

    server/red_channel: add red_channel_is_connected

diff --git a/server/red_channel.c b/server/red_channel.c
index be861fd..cab56a0 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -571,6 +571,11 @@ static PipeItem *red_channel_pipe_get(RedChannel *channel)
     return item;
 }
 
+int red_channel_is_connected(RedChannel *channel)
+{
+    return !!channel->peer;
+}
+
 static void red_channel_pipe_clear(RedChannel *channel)
 {
     PipeItem *item;
diff --git a/server/red_channel.h b/server/red_channel.h
index 24f969b..30adfc6 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -182,6 +182,8 @@ RedChannel *red_channel_create_parser(int size, RedsStreamContext *peer,
                                channel_on_incoming_error_proc incoming_error,
                                channel_on_outgoing_error_proc outgoing_error);
 
+int red_channel_is_connected(RedChannel *channel);
+
 void red_channel_destroy(RedChannel *channel);
 
 void red_channel_shutdown(RedChannel *channel);
commit 045be6b03ac114d53996df56a69f36fd7601edd3
Author: Alon Levy <alevy at redhat.com>
Date:   Thu Nov 4 16:03:25 2010 +0200

    server/inputs_channel: remove unused declaration in inputs_channel.h

diff --git a/server/inputs_channel.h b/server/inputs_channel.h
index 491c8a5..39d244b 100644
--- a/server/inputs_channel.h
+++ b/server/inputs_channel.h
@@ -28,7 +28,6 @@ void inputs_init(void);
 int inputs_inited(void);
 int inputs_has_tablet(void);
 const VDAgentMouseState *inputs_get_mouse_state(void);
-void inputs_send_keyboard_modifiers(uint8_t modifiers);
 void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds);
 int inputs_set_keyboard(SpiceKbdInstance *_keyboard);
 int inputs_set_mouse(SpiceMouseInstance *_mouse);
commit 6dfff7b0a532da3e220793f8528f34dc446365ed
Author: Alon Levy <alevy at redhat.com>
Date:   Thu Nov 4 15:59:46 2010 +0200

    server/inputs_channel: s/inputs_channel/g_inputs_channel/

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index d2818f2..f5f1d73 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -82,7 +82,7 @@ static SpiceTabletInstance *tablet = NULL;
 
 static SpiceTimer *key_modifiers_timer;
 
-static InputsChannel *inputs_channel = NULL;
+static InputsChannel *g_inputs_channel = NULL;
 
 #define KEY_MODIFIERS_TTL (1000 * 2) /*2sec*/
 
@@ -92,7 +92,7 @@ static InputsChannel *inputs_channel = NULL;
 
 int inputs_inited(void)
 {
-    return !!inputs_channel;
+    return !!g_inputs_channel;
 }
 
 int inputs_set_keyboard(SpiceKbdInstance *_keyboard)
@@ -149,8 +149,8 @@ void inputs_set_tablet_logical_size(int x_res, int y_res)
 
 const VDAgentMouseState *inputs_get_mouse_state(void)
 {
-    ASSERT(inputs_channel);
-    return &inputs_channel->mouse_state;
+    ASSERT(g_inputs_channel);
+    return &g_inputs_channel->mouse_state;
 }
 
 static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header)
@@ -261,9 +261,10 @@ static void inputs_channel_send_item(RedChannel *channel, PipeItem *base)
 
 static int inputs_channel_handle_parsed(RedChannel *channel, size_t size, uint32_t type, void *message)
 {
+    InputsChannel *inputs_channel = (InputsChannel *)channel;
     uint8_t *buf = (uint8_t *)message;
 
-    ASSERT(inputs_channel == (InputsChannel*)channel);
+    ASSERT(g_inputs_channel == inputs_channel);
     switch (type) {
     case SPICE_MSGC_INPUTS_KEY_DOWN: {
         SpiceMsgcKeyDown *key_up = (SpiceMsgcKeyDown *)buf;
@@ -426,22 +427,25 @@ static void inputs_channel_on_outgoing_error(RedChannel *channel)
 
 static void inputs_shutdown(Channel *channel)
 {
-    ASSERT(inputs_channel == (InputsChannel *)channel->data);
-    if (inputs_channel) {
-        red_channel_shutdown(&inputs_channel->base);
-        inputs_channel->base.incoming.shut = TRUE;
+    ASSERT(g_inputs_channel == (InputsChannel *)channel->data);
+    if (g_inputs_channel) {
+        red_channel_shutdown(&g_inputs_channel->base);
+        g_inputs_channel->base.incoming.shut = TRUE;
         channel->data = NULL;
-        inputs_channel = NULL;
+        g_inputs_channel = NULL;
     }
 }
 
 static void inputs_migrate(Channel *channel)
 {
-    InputsPipeItem *pipe_item = inputs_pipe_item_new(inputs_channel, PIPE_ITEM_MIGRATE);
-    SpiceMarshaller *m = pipe_item->m;
+    InputsChannel *inputs_channel = (InputsChannel *)channel->data;
+    InputsPipeItem *pipe_item;
+    SpiceMarshaller *m;
     SpiceMsgMigrate migrate;
 
-    ASSERT(inputs_channel == (InputsChannel *)channel->data);
+    ASSERT(g_inputs_channel == inputs_channel);
+    pipe_item = inputs_pipe_item_new(inputs_channel, PIPE_ITEM_MIGRATE);
+    m = pipe_item->m;
     migrate.flags = 0;
     spice_marshall_msg_migrate(m, &migrate);
     red_channel_pipe_add(&inputs_channel->base, &pipe_item->base);
@@ -481,10 +485,11 @@ static void inputs_link(Channel *channel, RedsStreamContext *peer, int migration
                         int num_common_caps, uint32_t *common_caps, int num_caps,
                         uint32_t *caps)
 {
+    InputsChannel *inputs_channel;
     red_printf("");
     ASSERT(channel->data == NULL);
 
-    inputs_channel = (InputsChannel*)red_channel_create_parser(
+    g_inputs_channel = inputs_channel = (InputsChannel*)red_channel_create_parser(
         sizeof(*inputs_channel), peer, core, migration, FALSE /* handle_acks */
         ,inputs_channel_config_socket
         ,spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL)
@@ -506,14 +511,14 @@ void inputs_send_keyboard_modifiers(uint8_t modifiers)
     InputsPipeItem *pipe_item;
     SpiceMarshaller *m;
 
-    if (!inputs_channel) {
+    if (!g_inputs_channel) {
         return;
     }
-    pipe_item = inputs_pipe_item_new(inputs_channel, PIPE_ITEM_KEY_MODIFIERS);
+    pipe_item = inputs_pipe_item_new(g_inputs_channel, PIPE_ITEM_KEY_MODIFIERS);
     m = pipe_item->m;
     key_modifiers.modifiers = modifiers;
     spice_marshall_msg_inputs_key_modifiers(m, &key_modifiers);
-    red_channel_pipe_add(&inputs_channel->base, &pipe_item->base);
+    red_channel_pipe_add(&g_inputs_channel->base, &pipe_item->base);
 }
 
 void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds)
commit 86a4a0780dcc82108f3e19fec0b6585e4cd23a46
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Nov 3 01:05:37 2010 +0200

    server/inputs_channel: initialize inputs_channel to NULL

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index 54b2e2d..d2818f2 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -82,7 +82,7 @@ static SpiceTabletInstance *tablet = NULL;
 
 static SpiceTimer *key_modifiers_timer;
 
-static InputsChannel *inputs_channel;
+static InputsChannel *inputs_channel = NULL;
 
 #define KEY_MODIFIERS_TTL (1000 * 2) /*2sec*/
 
commit 5e13eea73c4bcb620020592cfde16ad6d0c8f895
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Nov 3 01:03:44 2010 +0200

    server: reds/inputs_channel: move some structs to inputs_channel

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index bcd1a86..54b2e2d 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -44,6 +44,18 @@
 #define RECEIVE_BUF_SIZE \
     (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
 
+struct SpiceKbdState {
+    int dummy;
+};
+
+struct SpiceMouseState {
+    int dummy;
+};
+
+struct SpiceTabletState {
+    int dummy;
+};
+
 typedef struct InputsChannel {
     RedChannel base;
     uint8_t recv_buf[RECEIVE_BUF_SIZE];
diff --git a/server/reds.h b/server/reds.h
index 7f656e3..e440804 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -61,18 +61,6 @@ typedef struct Channel {
     void *data;
 } Channel;
 
-struct SpiceKbdState {
-    int dummy;
-};
-
-struct SpiceMouseState {
-    int dummy;
-};
-
-struct SpiceTabletState {
-    int dummy;
-};
-
 struct QXLState {
     QXLInterface          *qif;
     struct RedDispatcher  *dispatcher;
commit e662e6a59f21fb4ad2a9703a08ba79605ab0fa6c
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Nov 1 17:41:13 2010 +0200

    server: inputs_channel: use red_channel

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index f3e71f3..bcd1a86 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -28,9 +28,10 @@
 #include "server/demarshallers.h"
 #include "server/generated_marshallers.h"
 #include "spice.h"
-#include "inputs_channel.h"
 #include "red_common.h"
 #include "reds.h"
+#include "red_channel.h"
+#include "inputs_channel.h"
 
 // TODO: RECEIVE_BUF_SIZE used to be the same for inputs_channel and main_channel
 // since it was defined once in reds.c which contained both.
@@ -43,39 +44,26 @@
 #define RECEIVE_BUF_SIZE \
     (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
 
-#define SEND_BUF_SIZE 4096
-
-typedef struct IncomingHandler {
-    spice_parse_channel_func_t parser;
-    void *opaque;
-    int shut;
-    uint8_t buf[RECEIVE_BUF_SIZE];
-    uint32_t end_pos;
-    void (*handle_message)(void *opaque, size_t size, uint32_t type, void *message);
-} IncomingHandler;
-
-typedef struct OutgoingHandler {
-    void *opaque;
-    uint8_t buf[SEND_BUF_SIZE];
-    uint8_t *now;
-    uint32_t length;
-    void (*select)(void *opaque, int select);
-    void (*may_write)(void *opaque);
-} OutgoingHandler;
-
-
-// Temporarily here to make splitting reds.c to inputs_channel.c easier,
-// TODO - remove from here, leave private to inputs_channel.c
 typedef struct InputsChannel {
-    Channel *channel;
-    RedsStreamContext *peer;
-    IncomingHandler in_handler;
-    OutgoingHandler out_handler;
+    RedChannel base;
+    uint8_t recv_buf[RECEIVE_BUF_SIZE];
     VDAgentMouseState mouse_state;
     uint32_t motion_count;
-    uint64_t serial; //migrate me
 } InputsChannel;
 
+enum {
+    PIPE_ITEM_INIT = SPICE_MSG_INPUTS_INIT,
+    PIPE_ITEM_MOUSE_MOTION_ACK = SPICE_MSG_INPUTS_MOUSE_MOTION_ACK,
+    PIPE_ITEM_KEY_MODIFIERS = SPICE_MSG_INPUTS_KEY_MODIFIERS,
+    PIPE_ITEM_MIGRATE = SPICE_MSG_MIGRATE,
+};
+
+typedef struct InputsPipeItem {
+    PipeItem base;
+    SpiceMarshaller *m;
+    uint8_t *data;      /* If the marshaller malloced, pointer is here */
+} InputsPipeItem;
+
 static SpiceKbdInstance *keyboard = NULL;
 static SpiceMouseInstance *mouse = NULL;
 static SpiceTabletInstance *tablet = NULL;
@@ -153,132 +141,22 @@ const VDAgentMouseState *inputs_get_mouse_state(void)
     return &inputs_channel->mouse_state;
 }
 
-static int handle_incoming(RedsStreamContext *peer, IncomingHandler *handler)
+static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header)
 {
-    for (;;) {
-        uint8_t *buf = handler->buf;
-        uint32_t pos = handler->end_pos;
-        uint8_t *end = buf + pos;
-        SpiceDataHeader *header;
-        int n;
-        n = peer->cb_read(peer->ctx, buf + pos, RECEIVE_BUF_SIZE - pos);
-        if (n <= 0) {
-            if (n == 0) {
-                return -1;
-            }
-            switch (errno) {
-            case EAGAIN:
-                return 0;
-            case EINTR:
-                break;
-            case EPIPE:
-                return -1;
-            default:
-                red_printf("%s", strerror(errno));
-                return -1;
-            }
-        } else {
-            pos += n;
-            end = buf + pos;
-            while (buf + sizeof(SpiceDataHeader) <= end &&
-                   buf + sizeof(SpiceDataHeader) + (header = (SpiceDataHeader *)buf)->size <= end) {
-                uint8_t *data = (uint8_t *)(header+1);
-                size_t parsed_size;
-                uint8_t *parsed;
-                message_destructor_t parsed_free;
-
-
-                buf += sizeof(SpiceDataHeader) + header->size;
-                parsed = handler->parser(data, data + header->size, header->type,
-                                         SPICE_VERSION_MINOR, &parsed_size, &parsed_free);
-                if (parsed == NULL) {
-                    red_printf("failed to parse message type %d", header->type);
-                    return -1;
-                }
-                handler->handle_message(handler->opaque, parsed_size, header->type, parsed);
-                parsed_free(parsed);
-                if (handler->shut) {
-                    return -1;
-                }
-            }
-            memmove(handler->buf, buf, (handler->end_pos = end - buf));
-        }
-    }
+    InputsChannel *inputs_channel = SPICE_CONTAINEROF(channel, InputsChannel, base);
+
+    return inputs_channel->recv_buf;
 }
 
-static int handle_outgoing(RedsStreamContext *peer, OutgoingHandler *handler)
+static void inputs_channel_release_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header,
+                                               uint8_t *msg)
 {
-    if (!handler->length) {
-        return 0;
-    }
-
-    while (handler->length) {
-        int n;
-
-        n = peer->cb_write(peer->ctx, handler->now, handler->length);
-        if (n <= 0) {
-            if (n == 0) {
-                return -1;
-            }
-            switch (errno) {
-            case EAGAIN:
-                return 0;
-            case EINTR:
-                break;
-            case EPIPE:
-                return -1;
-            default:
-                red_printf("%s", strerror(errno));
-                return -1;
-            }
-        } else {
-            handler->now += n;
-            handler->length -= n;
-        }
-    }
-    handler->select(handler->opaque, FALSE);
-    handler->may_write(handler->opaque);
-    return 0;
 }
 
 #define OUTGOING_OK 0
 #define OUTGOING_FAILED -1
 #define OUTGOING_BLOCKED 1
 
-static int outgoing_write(RedsStreamContext *peer, OutgoingHandler *handler, void *in_data,
-                          int length)
-{
-    uint8_t *data = in_data;
-    ASSERT(length <= SEND_BUF_SIZE);
-    if (handler->length) {
-        return OUTGOING_BLOCKED;
-    }
-
-    while (length) {
-        int n = peer->cb_write(peer->ctx, data, length);
-        if (n < 0) {
-            switch (errno) {
-            case EAGAIN:
-                handler->length = length;
-                memcpy(handler->buf, data, length);
-                handler->select(handler->opaque, TRUE);
-                return OUTGOING_OK;
-            case EINTR:
-                break;
-            case EPIPE:
-                return OUTGOING_FAILED;
-            default:
-                red_printf("%s", strerror(errno));
-                return OUTGOING_FAILED;
-            }
-        } else {
-            data += n;
-            length -= n;
-        }
-    }
-    return OUTGOING_OK;
-}
-
 #define RED_MOUSE_STATE_TO_LOCAL(state)     \
     ((state & SPICE_MOUSE_BUTTON_MASK_LEFT) |          \
      ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) << 1) |   \
@@ -316,52 +194,64 @@ static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
     return sif->get_leds(sin);
 }
 
-static SpiceMarshaller *marshaller_new_for_outgoing(InputsChannel *state, int type)
+static InputsPipeItem *inputs_pipe_item_new(InputsChannel *channel, int type)
 {
-    SpiceMarshaller *m;
-    SpiceDataHeader *header;
+    InputsPipeItem *item = spice_malloc(sizeof(InputsPipeItem));
 
-    m = spice_marshaller_new();
-    header = (SpiceDataHeader *)
-        spice_marshaller_reserve_space(m, sizeof(SpiceDataHeader));
-    header->serial = ++state->serial;
-    header->type = type;
-    header->sub_list = 0;
+    red_channel_pipe_item_init(&channel->base, &item->base, type);
+    item->m = spice_marshaller_new();
+    item->data = NULL;
+    return item;
+}
 
-    return m;
+// Right now every PipeItem we add is an InputsPipeItem, later maybe make it simpler
+// for type only PipeItems
+static void inputs_pipe_add_type(InputsChannel *channel, int type)
+{
+    InputsPipeItem* pipe_item = inputs_pipe_item_new(channel, type);
+
+    red_channel_pipe_add(&channel->base, &pipe_item->base);
+}
+
+static void inputs_channel_release_pipe_item(RedChannel *channel,
+    PipeItem *base, int item_pushed)
+{
+    // All PipeItems we push are InputsPipeItem
+    InputsPipeItem *item = (InputsPipeItem*)base;
+
+    if (item->data) {
+        free(item->data);
+    }
+    spice_marshaller_destroy(item->m);
+    free(item);
 }
 
-static int marshaller_outgoing_write(SpiceMarshaller *m,
-                                     InputsChannel *state)
+static void inputs_channel_send_item(RedChannel *channel, PipeItem *base)
 {
-    SpiceDataHeader *header = (SpiceDataHeader *)spice_marshaller_get_ptr(m);
+    InputsPipeItem *item = SPICE_CONTAINEROF(base, InputsPipeItem, base);
+    SpiceMarshaller *m = item->m;
     uint8_t *data;
     size_t len;
     int free_data;
 
+    red_channel_reset_send_data(channel);
+    red_channel_init_send_data(channel, base->type, base);
     spice_marshaller_flush(m);
-    header->size = spice_marshaller_get_total_size(m) - sizeof(SpiceDataHeader);
-
+    // TODO: use spice_marshaller_fill_iovec. Right now we are doing something stupid,
+    // namely copying twice. See reds.c.
     data = spice_marshaller_linearize(m, 0, &len, &free_data);
-
-    if (outgoing_write(state->peer, &state->out_handler, data, len) != OUTGOING_OK) {
-        return FALSE;
+    item->data = (free_data && len > 0) ? data : NULL;
+    if (len > 0) {
+        red_channel_add_buf(channel, data, len);
     }
-
-    if (free_data) {
-        free(data);
-    }
-
-    return TRUE;
+    red_channel_begin_send_message(channel);
 }
 
-
-static void inputs_handle_input(void *opaque, size_t size, uint32_t type, void *message)
+static int inputs_channel_handle_parsed(RedChannel *channel, size_t size, uint32_t type, void *message)
 {
-    InputsChannel *state = (InputsChannel *)opaque;
     uint8_t *buf = (uint8_t *)message;
-    SpiceMarshaller *m;
 
+    ASSERT(inputs_channel == (InputsChannel*)channel);
     switch (type) {
     case SPICE_MSGC_INPUTS_KEY_DOWN: {
         SpiceMsgcKeyDown *key_up = (SpiceMsgcKeyDown *)buf;
@@ -382,13 +272,8 @@ static void inputs_handle_input(void *opaque, size_t size, uint32_t type, void *
     case SPICE_MSGC_INPUTS_MOUSE_MOTION: {
         SpiceMsgcMouseMotion *mouse_motion = (SpiceMsgcMouseMotion *)buf;
 
-        if (++state->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
-            m = marshaller_new_for_outgoing(state, SPICE_MSG_INPUTS_MOUSE_MOTION_ACK);
-            if (!marshaller_outgoing_write(m, state)) {
-                red_printf("motion ack failed");
-                reds_disconnect();
-            }
-            spice_marshaller_destroy(m);
+        if (++inputs_channel->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
+            inputs_pipe_add_type(inputs_channel, PIPE_ITEM_MOUSE_MOTION_ACK);
         }
         if (mouse && reds_get_mouse_mode() == SPICE_MOUSE_MODE_SERVER) {
             SpiceMouseInterface *sif;
@@ -402,13 +287,8 @@ static void inputs_handle_input(void *opaque, size_t size, uint32_t type, void *
     case SPICE_MSGC_INPUTS_MOUSE_POSITION: {
         SpiceMsgcMousePosition *pos = (SpiceMsgcMousePosition *)buf;
 
-        if (++state->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
-            m = marshaller_new_for_outgoing(state, SPICE_MSG_INPUTS_MOUSE_MOTION_ACK);
-            if (!marshaller_outgoing_write(m, state)) {
-                red_printf("position ack failed");
-                reds_disconnect();
-            }
-            spice_marshaller_destroy(m);
+        if (++inputs_channel->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
+            inputs_pipe_add_type(inputs_channel, PIPE_ITEM_MOUSE_MOTION_ACK);
         }
         if (reds_get_mouse_mode() != SPICE_MOUSE_MODE_CLIENT) {
             break;
@@ -420,7 +300,7 @@ static void inputs_handle_input(void *opaque, size_t size, uint32_t type, void *
             sif->position(tablet, pos->x, pos->y, RED_MOUSE_STATE_TO_LOCAL(pos->buttons_state));
             break;
         }
-        VDAgentMouseState *mouse_state = &state->mouse_state;
+        VDAgentMouseState *mouse_state = &inputs_channel->mouse_state;
         mouse_state->x = pos->x;
         mouse_state->y = pos->y;
         mouse_state->buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(pos->buttons_state);
@@ -506,7 +386,9 @@ static void inputs_handle_input(void *opaque, size_t size, uint32_t type, void *
         break;
     default:
         red_printf("unexpected type %d", type);
+        return FALSE;
     }
+    return TRUE;
 }
 
 static void inputs_relase_keys(void)
@@ -519,146 +401,107 @@ static void inputs_relase_keys(void)
     kbd_push_scan(keyboard, 0x38 | 0x80); //LALT
 }
 
-static void inputs_event(int fd, int event, void *data)
+static void inputs_channel_on_incoming_error(RedChannel *channel)
 {
-    if (data != inputs_channel) {
-        return; // shutdown already happened
-    }
-    if (event & SPICE_WATCH_EVENT_READ) {
-        if (handle_incoming(inputs_channel->peer, &inputs_channel->in_handler)) {
-            inputs_relase_keys();
-            core->watch_remove(inputs_channel->peer->watch);
-            inputs_channel->peer->watch = NULL;
-            if (inputs_channel->channel) {
-                inputs_channel->channel->data = NULL;
-            }
-            inputs_channel->peer->cb_free(inputs_channel->peer);
-            free(inputs_channel);
-            inputs_channel = NULL;
-        }
-    }
-    if (event & SPICE_WATCH_EVENT_WRITE) {
-        if (handle_outgoing(inputs_channel->peer, &inputs_channel->out_handler)) {
-            reds_disconnect();
-        }
-    }
+    inputs_relase_keys();
+    red_channel_destroy(channel);
 }
 
+static void inputs_channel_on_outgoing_error(RedChannel *channel)
+{
+    reds_disconnect();
+}
 
 static void inputs_shutdown(Channel *channel)
 {
-    InputsChannel *state = (InputsChannel *)channel->data;
-    if (state) {
-        state->in_handler.shut = TRUE;
-        shutdown(state->peer->socket, SHUT_RDWR);
+    ASSERT(inputs_channel == (InputsChannel *)channel->data);
+    if (inputs_channel) {
+        red_channel_shutdown(&inputs_channel->base);
+        inputs_channel->base.incoming.shut = TRUE;
         channel->data = NULL;
-        state->channel = NULL;
         inputs_channel = NULL;
     }
 }
 
 static void inputs_migrate(Channel *channel)
 {
-    InputsChannel *state = (InputsChannel *)channel->data;
-    SpiceMarshaller *m;
+    InputsPipeItem *pipe_item = inputs_pipe_item_new(inputs_channel, PIPE_ITEM_MIGRATE);
+    SpiceMarshaller *m = pipe_item->m;
     SpiceMsgMigrate migrate;
 
-    m = marshaller_new_for_outgoing(state, SPICE_MSG_MIGRATE);
-
+    ASSERT(inputs_channel == (InputsChannel *)channel->data);
     migrate.flags = 0;
     spice_marshall_msg_migrate(m, &migrate);
-
-    if (!marshaller_outgoing_write(m, state)) {
-        red_printf("write failed");
-    }
-    spice_marshaller_destroy(m);
+    red_channel_pipe_add(&inputs_channel->base, &pipe_item->base);
 }
 
-static void inputs_select(void *opaque, int select)
+static void inputs_pipe_add_init(InputsChannel *channel)
 {
-    int eventmask = SPICE_WATCH_EVENT_READ;
-    red_printf("");
+    SpiceMsgInputsInit inputs_init;
+    InputsPipeItem *pipe_item = inputs_pipe_item_new(channel, PIPE_ITEM_INIT);
+    SpiceMarshaller *m = pipe_item->m;
 
-    ASSERT(opaque == inputs_channel);
-    if (select) {
-        eventmask |= SPICE_WATCH_EVENT_WRITE;
-    }
-    core->watch_update_mask(inputs_channel->peer->watch, eventmask);
+    inputs_init.keyboard_modifiers = kbd_get_leds(keyboard);
+    spice_marshall_msg_inputs_init(m, &inputs_init);
+    red_channel_pipe_add(&channel->base, &pipe_item->base);
 }
 
-static void inputs_may_write(void *opaque)
+static int inputs_channel_config_socket(RedChannel *channel)
 {
-    red_printf("");
+    int flags;
+    int delay_val = 1;
+
+    if (setsockopt(channel->peer->socket, IPPROTO_TCP, TCP_NODELAY,
+            &delay_val, sizeof(delay_val)) == -1) {
+        red_printf("setsockopt failed, %s", strerror(errno));
+        return FALSE;
+    }
+
+    if ((flags = fcntl(channel->peer->socket, F_GETFL)) == -1 ||
+                 fcntl(channel->peer->socket, F_SETFL, flags | O_ASYNC) == -1) {
+        red_printf("fcntl failed, %s", strerror(errno));
+        return FALSE;
+    }
+    return TRUE;
 }
 
 static void inputs_link(Channel *channel, RedsStreamContext *peer, int migration,
                         int num_common_caps, uint32_t *common_caps, int num_caps,
                         uint32_t *caps)
 {
-    int delay_val;
-    int flags;
-
     red_printf("");
     ASSERT(channel->data == NULL);
 
-    inputs_channel = spice_new0(InputsChannel, 1);
-
-    delay_val = 1;
-    if (setsockopt(peer->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) {
-        red_printf("setsockopt failed, %s", strerror(errno));
-    }
-
-    if ((flags = fcntl(peer->socket, F_GETFL)) == -1 ||
-                                            fcntl(peer->socket, F_SETFL, flags | O_ASYNC) == -1) {
-        red_printf("fcntl failed, %s", strerror(errno));
-    }
-
-    inputs_channel->peer = peer;
-    inputs_channel->channel = channel;
-    inputs_channel->in_handler.parser = spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL);
-    inputs_channel->in_handler.opaque = inputs_channel;
-    inputs_channel->in_handler.handle_message = inputs_handle_input;
-    inputs_channel->out_handler.length = 0;
-    inputs_channel->out_handler.opaque = inputs_channel;
-    inputs_channel->out_handler.select = inputs_select;
-    inputs_channel->out_handler.may_write = inputs_may_write;
+    inputs_channel = (InputsChannel*)red_channel_create_parser(
+        sizeof(*inputs_channel), peer, core, migration, FALSE /* handle_acks */
+        ,inputs_channel_config_socket
+        ,spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL)
+        ,inputs_channel_handle_parsed
+        ,inputs_channel_alloc_msg_rcv_buf
+        ,inputs_channel_release_msg_rcv_buf
+        ,inputs_channel_send_item
+        ,inputs_channel_release_pipe_item
+        ,inputs_channel_on_incoming_error
+        ,inputs_channel_on_outgoing_error);
+    ASSERT(inputs_channel);
     channel->data = inputs_channel;
-    peer->watch = core->watch_add(peer->socket, SPICE_WATCH_EVENT_READ,
-                                  inputs_event, inputs_channel);
-
-    SpiceMarshaller *m;
-    SpiceMsgInputsInit inputs_init;
-    m = marshaller_new_for_outgoing(inputs_channel, SPICE_MSG_INPUTS_INIT);
-    inputs_init.keyboard_modifiers = kbd_get_leds(keyboard);
-    spice_marshall_msg_inputs_init(m, &inputs_init);
-    if (!marshaller_outgoing_write(m, inputs_channel)) {
-        red_printf("failed to send modifiers state");
-        reds_disconnect();
-    }
-    spice_marshaller_destroy(m);
+    inputs_pipe_add_init(inputs_channel);
 }
 
 void inputs_send_keyboard_modifiers(uint8_t modifiers)
 {
     SpiceMsgInputsKeyModifiers key_modifiers;
+    InputsPipeItem *pipe_item;
     SpiceMarshaller *m;
 
     if (!inputs_channel) {
         return;
     }
-    ASSERT(inputs_channel->peer);
-
-    m = marshaller_new_for_outgoing(inputs_channel,
-                    SPICE_MSG_INPUTS_KEY_MODIFIERS);
-
+    pipe_item = inputs_pipe_item_new(inputs_channel, PIPE_ITEM_KEY_MODIFIERS);
+    m = pipe_item->m;
     key_modifiers.modifiers = modifiers;
     spice_marshall_msg_inputs_key_modifiers(m, &key_modifiers);
-
-    if (!marshaller_outgoing_write(m, inputs_channel)) {
-        red_printf("failed to send modifiers state");
-        reds_disconnect();
-    }
-    spice_marshaller_destroy(m);
+    red_channel_pipe_add(&inputs_channel->base, &pipe_item->base);
 }
 
 void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds)
commit 23db8c093c043127310e41874fd428e4b24b2d9d
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Nov 1 17:40:46 2010 +0200

    server: red_channel: add optional parser and separate incoming/outgoing error handlers for later inputs/main channel usage

diff --git a/server/red_channel.c b/server/red_channel.c
index f547f5a..be861fd 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -68,6 +68,9 @@ static int red_peer_receive(RedsStreamContext *peer, uint8_t *buf, uint32_t size
 static void red_peer_handle_incoming(RedsStreamContext *peer, IncomingHandler *handler)
 {
     int bytes_read;
+    uint8_t *parsed;
+    size_t parsed_size;
+    message_destructor_t parsed_free;
 
     for (;;) {
         int ret_handle;
@@ -105,8 +108,25 @@ static void red_peer_handle_incoming(RedsStreamContext *peer, IncomingHandler *h
             }
         }
 
-        ret_handle = handler->handle_message(handler->opaque, &handler->header,
-                                             handler->msg);
+        if (handler->parser) {
+            parsed = handler->parser(handler->msg, handler->msg + handler->header.size, handler->header.type,
+                                     SPICE_VERSION_MINOR, &parsed_size, &parsed_free);
+            if (parsed == NULL) {
+                red_printf("failed to parse message type %d", handler->header.type);
+                handler->on_error(handler->opaque);
+                return;
+            }
+            ret_handle = handler->handle_parsed(handler->opaque, parsed_size,
+                                    handler->header.type, parsed);
+            parsed_free(parsed);
+        } else {
+            ret_handle = handler->handle_message(handler->opaque, &handler->header,
+                                                 handler->msg);
+        }
+        if (handler->shut) {
+            handler->on_error(handler->opaque);
+            return;
+        }
         handler->msg_pos = 0;
         handler->msg = NULL;
         handler->header_pos = 0;
@@ -183,13 +203,27 @@ static void red_peer_handle_outgoing(RedsStreamContext *peer, OutgoingHandler *h
 
 static inline void red_channel_fill_iovec(RedChannel *channel, struct iovec *vec, int *vec_size);
 
-
 static void red_channel_peer_on_error(void *opaque)
 {
     RedChannel *channel = (RedChannel *)opaque;
+
     channel->disconnect(channel);
 }
 
+static void red_channel_peer_on_incoming_error(void *opaque)
+{
+    RedChannel *channel = (RedChannel *)opaque;
+
+    channel->on_incoming_error(channel);
+}
+
+static void red_channel_peer_on_outgoing_error(void *opaque)
+{
+    RedChannel *channel = (RedChannel *)opaque;
+
+    channel->on_outgoing_error(channel);
+}
+
 static int red_channel_peer_get_out_msg_size(void *opaque)
 {
     RedChannel *channel = (RedChannel *)opaque;
@@ -276,6 +310,8 @@ RedChannel *red_channel_create(int size, RedsStreamContext *peer,
     channel->outgoing.on_error = red_channel_peer_on_error;
     channel->outgoing.on_msg_done = red_channel_peer_on_out_msg_done;
 
+    channel->shut = 0; // came here from inputs, perhaps can be removed? XXX
+
     if (!config_socket(channel)) {
         goto error;
     }
@@ -293,6 +329,44 @@ error:
     return NULL;
 }
 
+void do_nothing_disconnect(RedChannel *red_channel)
+{
+}
+
+int do_nothing_handle_message(RedChannel *red_channel, SpiceDataHeader *header, uint8_t *msg)
+{
+    return TRUE;
+}
+
+RedChannel *red_channel_create_parser(int size, RedsStreamContext *peer,
+                               SpiceCoreInterface *core,
+                               int migrate, int handle_acks,
+                               channel_configure_socket_proc config_socket,
+                               spice_parse_channel_func_t parser,
+                               channel_handle_parsed_proc handle_parsed,
+                               channel_alloc_msg_recv_buf_proc alloc_recv_buf,
+                               channel_release_msg_recv_buf_proc release_recv_buf,
+                               channel_send_pipe_item_proc send_item,
+                               channel_release_pipe_item_proc release_item,
+                               channel_on_incoming_error_proc incoming_error,
+                               channel_on_outgoing_error_proc outgoing_error)
+{
+    RedChannel *channel = red_channel_create(size, peer,
+        core, migrate, handle_acks, config_socket, do_nothing_disconnect, do_nothing_handle_message,
+        alloc_recv_buf, release_recv_buf, send_item, release_item);
+
+    if (channel == NULL) {
+        return NULL;
+    }
+    channel->incoming.handle_parsed = (handle_parsed_proc)handle_parsed;
+    channel->incoming.parser = parser;
+    channel->on_incoming_error = incoming_error;
+    channel->on_outgoing_error = outgoing_error;
+    channel->incoming.on_error = red_channel_peer_on_incoming_error;
+    channel->outgoing.on_error = red_channel_peer_on_outgoing_error;
+    return channel;
+}
+
 void red_channel_destroy(RedChannel *channel)
 {
     if (!channel) {
diff --git a/server/red_channel.h b/server/red_channel.h
index 509da77..24f969b 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -26,6 +26,7 @@
 #include "reds.h"
 #include "spice.h"
 #include "ring.h"
+#include "server/demarshallers.h"
 
 #define MAX_SEND_BUFS 1000
 #define MAX_SEND_VEC 50
@@ -37,6 +38,7 @@
 
 typedef int (*handle_message_proc)(void *opaque,
                                    SpiceDataHeader *header, uint8_t *msg);
+typedef int (*handle_parsed_proc)(void *opaque, size_t size, uint32_t type, void *message);
 typedef uint8_t *(*alloc_msg_recv_buf_proc)(void *opaque, SpiceDataHeader *msg_header);
 typedef void (*release_msg_recv_buf_proc)(void *opaque,
                                           SpiceDataHeader *msg_header, uint8_t *msg);
@@ -52,6 +54,10 @@ typedef struct IncomingHandler {
     alloc_msg_recv_buf_proc alloc_msg_buf;
     on_incoming_error_proc on_error; // recv error or handle_message error
     release_msg_recv_buf_proc release_msg_buf; // for errors
+    // The following is an optional alternative to handle_message, used if not null
+    spice_parse_channel_func_t parser;
+    handle_parsed_proc handle_parsed;
+    int shut; // came here from inputs_channel. Not sure if it is really required or can be removed. XXX
 } IncomingHandler;
 
 typedef int (*get_outgoing_msg_size_proc)(void *opaque);
@@ -89,6 +95,8 @@ typedef struct RedChannel RedChannel;
 
 typedef uint8_t *(*channel_alloc_msg_recv_buf_proc)(RedChannel *channel,
                                                     SpiceDataHeader *msg_header);
+typedef int (*channel_handle_parsed_proc)(RedChannel *channel, size_t size, uint32_t type,
+                                        void *message);
 typedef int (*channel_handle_message_proc)(RedChannel *channel,
                                            SpiceDataHeader *header, uint8_t *msg);
 typedef void (*channel_release_msg_recv_buf_proc)(RedChannel *channel,
@@ -98,6 +106,8 @@ typedef int (*channel_configure_socket_proc)(RedChannel *channel);
 typedef void (*channel_send_pipe_item_proc)(RedChannel *channel, PipeItem *item);
 typedef void (*channel_release_pipe_item_proc)(RedChannel *channel,
                                                PipeItem *item, int item_pushed);
+typedef void (*channel_on_incoming_error_proc)(RedChannel *channel);
+typedef void (*channel_on_outgoing_error_proc)(RedChannel *channel);
 
 struct RedChannel {
     RedsStreamContext *peer;
@@ -137,6 +147,11 @@ struct RedChannel {
     channel_release_pipe_item_proc release_item;
 
     int during_send;
+    /* Stuff below added for Main and Inputs channels switch to RedChannel
+     * (might be removed later) */
+    channel_on_incoming_error_proc on_incoming_error; /* alternative to disconnect */
+    channel_on_outgoing_error_proc on_outgoing_error;
+    int shut; /* signal channel is to be closed */
 };
 
 /* if one of the callbacks should cause disconnect, use red_channel_shutdown and don't
@@ -152,6 +167,21 @@ RedChannel *red_channel_create(int size, RedsStreamContext *peer,
                                channel_send_pipe_item_proc send_item,
                                channel_release_pipe_item_proc release_item);
 
+/* alternative constructor, meant for marshaller based (inputs,main) channels,
+ * will become default eventually */
+RedChannel *red_channel_create_parser(int size, RedsStreamContext *peer,
+                               SpiceCoreInterface *core,
+                               int migrate, int handle_acks,
+                               channel_configure_socket_proc config_socket,
+                               spice_parse_channel_func_t parser,
+                               channel_handle_parsed_proc handle_parsed,
+                               channel_alloc_msg_recv_buf_proc alloc_recv_buf,
+                               channel_release_msg_recv_buf_proc release_recv_buf,
+                               channel_send_pipe_item_proc send_item,
+                               channel_release_pipe_item_proc release_item,
+                               channel_on_incoming_error_proc incoming_error,
+                               channel_on_outgoing_error_proc outgoing_error);
+
 void red_channel_destroy(RedChannel *channel);
 
 void red_channel_shutdown(RedChannel *channel);
commit 5ada644f3037ad768a54eef2c5b38dd2d7c590d8
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Oct 31 17:19:49 2010 +0200

    server: inputs_channel: s/inputs_state/inputs_channel/

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index 288724e..f3e71f3 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -66,7 +66,7 @@ typedef struct OutgoingHandler {
 
 // Temporarily here to make splitting reds.c to inputs_channel.c easier,
 // TODO - remove from here, leave private to inputs_channel.c
-typedef struct InputsState {
+typedef struct InputsChannel {
     Channel *channel;
     RedsStreamContext *peer;
     IncomingHandler in_handler;
@@ -74,11 +74,7 @@ typedef struct InputsState {
     VDAgentMouseState mouse_state;
     uint32_t motion_count;
     uint64_t serial; //migrate me
-} InputsState;
-
-
-// TODO: move to InputsState after InputsState lands here
-// from reds_inputs.h
+} InputsChannel;
 
 static SpiceKbdInstance *keyboard = NULL;
 static SpiceMouseInstance *mouse = NULL;
@@ -86,7 +82,7 @@ static SpiceTabletInstance *tablet = NULL;
 
 static SpiceTimer *key_modifiers_timer;
 
-static InputsState *inputs_state;
+static InputsChannel *inputs_channel;
 
 #define KEY_MODIFIERS_TTL (1000 * 2) /*2sec*/
 
@@ -96,7 +92,7 @@ static InputsState *inputs_state;
 
 int inputs_inited(void)
 {
-    return !!inputs_state;
+    return !!inputs_channel;
 }
 
 int inputs_set_keyboard(SpiceKbdInstance *_keyboard)
@@ -153,8 +149,8 @@ void inputs_set_tablet_logical_size(int x_res, int y_res)
 
 const VDAgentMouseState *inputs_get_mouse_state(void)
 {
-    ASSERT(inputs_state);
-    return &inputs_state->mouse_state;
+    ASSERT(inputs_channel);
+    return &inputs_channel->mouse_state;
 }
 
 static int handle_incoming(RedsStreamContext *peer, IncomingHandler *handler)
@@ -320,7 +316,7 @@ static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
     return sif->get_leds(sin);
 }
 
-static SpiceMarshaller *marshaller_new_for_outgoing(InputsState *state, int type)
+static SpiceMarshaller *marshaller_new_for_outgoing(InputsChannel *state, int type)
 {
     SpiceMarshaller *m;
     SpiceDataHeader *header;
@@ -336,7 +332,7 @@ static SpiceMarshaller *marshaller_new_for_outgoing(InputsState *state, int type
 }
 
 static int marshaller_outgoing_write(SpiceMarshaller *m,
-                                     InputsState *state)
+                                     InputsChannel *state)
 {
     SpiceDataHeader *header = (SpiceDataHeader *)spice_marshaller_get_ptr(m);
     uint8_t *data;
@@ -362,7 +358,7 @@ static int marshaller_outgoing_write(SpiceMarshaller *m,
 
 static void inputs_handle_input(void *opaque, size_t size, uint32_t type, void *message)
 {
-    InputsState *state = (InputsState *)opaque;
+    InputsChannel *state = (InputsChannel *)opaque;
     uint8_t *buf = (uint8_t *)message;
     SpiceMarshaller *m;
 
@@ -442,11 +438,11 @@ static void inputs_handle_input(void *opaque, size_t size, uint32_t type, void *
         }
         if (reds_get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
             if (reds_get_agent_mouse() && reds_has_vdagent()) {
-                inputs_state->mouse_state.buttons =
+                inputs_channel->mouse_state.buttons =
                     RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_press->buttons_state) |
                     (dz == -1 ? VD_AGENT_UBUTTON_MASK : 0) |
                     (dz == 1 ? VD_AGENT_DBUTTON_MASK : 0);
-                reds_handle_agent_mouse_event(&inputs_state->mouse_state);
+                reds_handle_agent_mouse_event(&inputs_channel->mouse_state);
             } else if (tablet) {
                 SpiceTabletInterface *sif;
                 sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
@@ -464,9 +460,9 @@ static void inputs_handle_input(void *opaque, size_t size, uint32_t type, void *
         SpiceMsgcMouseRelease *mouse_release = (SpiceMsgcMouseRelease *)buf;
         if (reds_get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
             if (reds_get_agent_mouse() && reds_has_vdagent()) {
-                inputs_state->mouse_state.buttons =
+                inputs_channel->mouse_state.buttons =
                     RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_release->buttons_state);
-                reds_handle_agent_mouse_event(&inputs_state->mouse_state);
+                reds_handle_agent_mouse_event(&inputs_channel->mouse_state);
             } else if (tablet) {
                 SpiceTabletInterface *sif;
                 sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
@@ -525,25 +521,24 @@ static void inputs_relase_keys(void)
 
 static void inputs_event(int fd, int event, void *data)
 {
-    if (data != inputs_state) {
+    if (data != inputs_channel) {
         return; // shutdown already happened
     }
-
     if (event & SPICE_WATCH_EVENT_READ) {
-        if (handle_incoming(inputs_state->peer, &inputs_state->in_handler)) {
+        if (handle_incoming(inputs_channel->peer, &inputs_channel->in_handler)) {
             inputs_relase_keys();
-            core->watch_remove(inputs_state->peer->watch);
-            inputs_state->peer->watch = NULL;
-            if (inputs_state->channel) {
-                inputs_state->channel->data = NULL;
+            core->watch_remove(inputs_channel->peer->watch);
+            inputs_channel->peer->watch = NULL;
+            if (inputs_channel->channel) {
+                inputs_channel->channel->data = NULL;
             }
-            inputs_state->peer->cb_free(inputs_state->peer);
-            free(inputs_state);
-            inputs_state = NULL;
+            inputs_channel->peer->cb_free(inputs_channel->peer);
+            free(inputs_channel);
+            inputs_channel = NULL;
         }
     }
     if (event & SPICE_WATCH_EVENT_WRITE) {
-        if (handle_outgoing(inputs_state->peer, &inputs_state->out_handler)) {
+        if (handle_outgoing(inputs_channel->peer, &inputs_channel->out_handler)) {
             reds_disconnect();
         }
     }
@@ -552,19 +547,19 @@ static void inputs_event(int fd, int event, void *data)
 
 static void inputs_shutdown(Channel *channel)
 {
-    InputsState *state = (InputsState *)channel->data;
+    InputsChannel *state = (InputsChannel *)channel->data;
     if (state) {
         state->in_handler.shut = TRUE;
         shutdown(state->peer->socket, SHUT_RDWR);
         channel->data = NULL;
         state->channel = NULL;
-        inputs_state = NULL;
+        inputs_channel = NULL;
     }
 }
 
 static void inputs_migrate(Channel *channel)
 {
-    InputsState *state = (InputsState *)channel->data;
+    InputsChannel *state = (InputsChannel *)channel->data;
     SpiceMarshaller *m;
     SpiceMsgMigrate migrate;
 
@@ -584,11 +579,11 @@ static void inputs_select(void *opaque, int select)
     int eventmask = SPICE_WATCH_EVENT_READ;
     red_printf("");
 
-    ASSERT(opaque == inputs_state);
+    ASSERT(opaque == inputs_channel);
     if (select) {
         eventmask |= SPICE_WATCH_EVENT_WRITE;
     }
-    core->watch_update_mask(inputs_state->peer->watch, eventmask);
+    core->watch_update_mask(inputs_channel->peer->watch, eventmask);
 }
 
 static void inputs_may_write(void *opaque)
@@ -606,7 +601,7 @@ static void inputs_link(Channel *channel, RedsStreamContext *peer, int migration
     red_printf("");
     ASSERT(channel->data == NULL);
 
-    inputs_state = spice_new0(InputsState, 1);
+    inputs_channel = spice_new0(InputsChannel, 1);
 
     delay_val = 1;
     if (setsockopt(peer->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) {
@@ -618,25 +613,25 @@ static void inputs_link(Channel *channel, RedsStreamContext *peer, int migration
         red_printf("fcntl failed, %s", strerror(errno));
     }
 
-    inputs_state->peer = peer;
-    inputs_state->channel = channel;
-    inputs_state->in_handler.parser = spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL);
-    inputs_state->in_handler.opaque = inputs_state;
-    inputs_state->in_handler.handle_message = inputs_handle_input;
-    inputs_state->out_handler.length = 0;
-    inputs_state->out_handler.opaque = inputs_state;
-    inputs_state->out_handler.select = inputs_select;
-    inputs_state->out_handler.may_write = inputs_may_write;
-    channel->data = inputs_state;
+    inputs_channel->peer = peer;
+    inputs_channel->channel = channel;
+    inputs_channel->in_handler.parser = spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL);
+    inputs_channel->in_handler.opaque = inputs_channel;
+    inputs_channel->in_handler.handle_message = inputs_handle_input;
+    inputs_channel->out_handler.length = 0;
+    inputs_channel->out_handler.opaque = inputs_channel;
+    inputs_channel->out_handler.select = inputs_select;
+    inputs_channel->out_handler.may_write = inputs_may_write;
+    channel->data = inputs_channel;
     peer->watch = core->watch_add(peer->socket, SPICE_WATCH_EVENT_READ,
-                                  inputs_event, inputs_state);
+                                  inputs_event, inputs_channel);
 
     SpiceMarshaller *m;
     SpiceMsgInputsInit inputs_init;
-    m = marshaller_new_for_outgoing(inputs_state, SPICE_MSG_INPUTS_INIT);
+    m = marshaller_new_for_outgoing(inputs_channel, SPICE_MSG_INPUTS_INIT);
     inputs_init.keyboard_modifiers = kbd_get_leds(keyboard);
     spice_marshall_msg_inputs_init(m, &inputs_init);
-    if (!marshaller_outgoing_write(m, inputs_state)) {
+    if (!marshaller_outgoing_write(m, inputs_channel)) {
         red_printf("failed to send modifiers state");
         reds_disconnect();
     }
@@ -648,18 +643,18 @@ void inputs_send_keyboard_modifiers(uint8_t modifiers)
     SpiceMsgInputsKeyModifiers key_modifiers;
     SpiceMarshaller *m;
 
-    if (!inputs_state) {
+    if (!inputs_channel) {
         return;
     }
-    ASSERT(inputs_state->peer);
+    ASSERT(inputs_channel->peer);
 
-    m = marshaller_new_for_outgoing(inputs_state,
+    m = marshaller_new_for_outgoing(inputs_channel,
                     SPICE_MSG_INPUTS_KEY_MODIFIERS);
 
     key_modifiers.modifiers = modifiers;
     spice_marshall_msg_inputs_key_modifiers(m, &key_modifiers);
 
-    if (!marshaller_outgoing_write(m, inputs_state)) {
+    if (!marshaller_outgoing_write(m, inputs_channel)) {
         red_printf("failed to send modifiers state");
         reds_disconnect();
     }
commit 347e32177cd7a563054ac903e1efb193f6ec7b57
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Oct 31 16:31:14 2010 +0200

    server: introduce inputs_channel, split from reds.c

diff --git a/server/Makefile.am b/server/Makefile.am
index 2c86a2c..ab66ba7 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -114,6 +114,7 @@ libspice_server_la_SOURCES =			\
 	red_parse_qxl.c				\
 	red_parse_qxl.h				\
 	reds.c					\
+	inputs_channel.c			\
 	reds.h					\
 	stat.h					\
 	red_worker.c				\
diff --git a/server/inputs_channel.c b/server/inputs_channel.c
new file mode 100644
index 0000000..288724e
--- /dev/null
+++ b/server/inputs_channel.c
@@ -0,0 +1,694 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <netinet/in.h> // IPPROTO_TCP
+#include <netinet/tcp.h> // TCP_NODELAY
+#include <fcntl.h>
+#include <stddef.h> // NULL
+#include <errno.h>
+#include <spice/macros.h>
+#include <spice/vd_agent.h>
+#include "common/marshaller.h"
+#include "common/messages.h"
+#include "server/demarshallers.h"
+#include "server/generated_marshallers.h"
+#include "spice.h"
+#include "inputs_channel.h"
+#include "red_common.h"
+#include "reds.h"
+
+// TODO: RECEIVE_BUF_SIZE used to be the same for inputs_channel and main_channel
+// since it was defined once in reds.c which contained both.
+// Now that they are split we can give a more fitting value for inputs - what
+// should it be?
+#define REDS_AGENT_WINDOW_SIZE 10
+#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
+
+// approximate max receive message size
+#define RECEIVE_BUF_SIZE \
+    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
+
+#define SEND_BUF_SIZE 4096
+
+typedef struct IncomingHandler {
+    spice_parse_channel_func_t parser;
+    void *opaque;
+    int shut;
+    uint8_t buf[RECEIVE_BUF_SIZE];
+    uint32_t end_pos;
+    void (*handle_message)(void *opaque, size_t size, uint32_t type, void *message);
+} IncomingHandler;
+
+typedef struct OutgoingHandler {
+    void *opaque;
+    uint8_t buf[SEND_BUF_SIZE];
+    uint8_t *now;
+    uint32_t length;
+    void (*select)(void *opaque, int select);
+    void (*may_write)(void *opaque);
+} OutgoingHandler;
+
+
+// Temporarily here to make splitting reds.c to inputs_channel.c easier,
+// TODO - remove from here, leave private to inputs_channel.c
+typedef struct InputsState {
+    Channel *channel;
+    RedsStreamContext *peer;
+    IncomingHandler in_handler;
+    OutgoingHandler out_handler;
+    VDAgentMouseState mouse_state;
+    uint32_t motion_count;
+    uint64_t serial; //migrate me
+} InputsState;
+
+
+// TODO: move to InputsState after InputsState lands here
+// from reds_inputs.h
+
+static SpiceKbdInstance *keyboard = NULL;
+static SpiceMouseInstance *mouse = NULL;
+static SpiceTabletInstance *tablet = NULL;
+
+static SpiceTimer *key_modifiers_timer;
+
+static InputsState *inputs_state;
+
+#define KEY_MODIFIERS_TTL (1000 * 2) /*2sec*/
+
+#define SCROLL_LOCK_SCAN_CODE 0x46
+#define NUM_LOCK_SCAN_CODE 0x45
+#define CAPS_LOCK_SCAN_CODE 0x3a
+
+int inputs_inited(void)
+{
+    return !!inputs_state;
+}
+
+int inputs_set_keyboard(SpiceKbdInstance *_keyboard)
+{
+    if (keyboard) {
+        red_printf("already have keyboard");
+        return -1;
+    }
+    keyboard = _keyboard;
+    keyboard->st = spice_new0(SpiceKbdState, 1);
+    return 0;
+}
+
+int inputs_set_mouse(SpiceMouseInstance *_mouse)
+{
+    if (mouse) {
+        red_printf("already have mouse");
+        return -1;
+    }
+    mouse = _mouse;
+    mouse->st = spice_new0(SpiceMouseState, 1);
+    return 0;
+}
+
+int inputs_set_tablet(SpiceTabletInstance *_tablet)
+{
+    if (tablet) {
+        red_printf("already have tablet");
+        return -1;
+    }
+    tablet = _tablet;
+    tablet->st = spice_new0(SpiceTabletState, 1);
+    return 0;
+}
+
+int inputs_has_tablet(void)
+{
+    return !!tablet;
+}
+
+void inputs_detach_tablet(SpiceTabletInstance *_tablet)
+{
+    red_printf("");
+    tablet = NULL;
+}
+
+void inputs_set_tablet_logical_size(int x_res, int y_res)
+{
+    SpiceTabletInterface *sif;
+
+    sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
+    sif->set_logical_size(tablet, x_res, y_res);
+}
+
+const VDAgentMouseState *inputs_get_mouse_state(void)
+{
+    ASSERT(inputs_state);
+    return &inputs_state->mouse_state;
+}
+
+static int handle_incoming(RedsStreamContext *peer, IncomingHandler *handler)
+{
+    for (;;) {
+        uint8_t *buf = handler->buf;
+        uint32_t pos = handler->end_pos;
+        uint8_t *end = buf + pos;
+        SpiceDataHeader *header;
+        int n;
+        n = peer->cb_read(peer->ctx, buf + pos, RECEIVE_BUF_SIZE - pos);
+        if (n <= 0) {
+            if (n == 0) {
+                return -1;
+            }
+            switch (errno) {
+            case EAGAIN:
+                return 0;
+            case EINTR:
+                break;
+            case EPIPE:
+                return -1;
+            default:
+                red_printf("%s", strerror(errno));
+                return -1;
+            }
+        } else {
+            pos += n;
+            end = buf + pos;
+            while (buf + sizeof(SpiceDataHeader) <= end &&
+                   buf + sizeof(SpiceDataHeader) + (header = (SpiceDataHeader *)buf)->size <= end) {
+                uint8_t *data = (uint8_t *)(header+1);
+                size_t parsed_size;
+                uint8_t *parsed;
+                message_destructor_t parsed_free;
+
+
+                buf += sizeof(SpiceDataHeader) + header->size;
+                parsed = handler->parser(data, data + header->size, header->type,
+                                         SPICE_VERSION_MINOR, &parsed_size, &parsed_free);
+                if (parsed == NULL) {
+                    red_printf("failed to parse message type %d", header->type);
+                    return -1;
+                }
+                handler->handle_message(handler->opaque, parsed_size, header->type, parsed);
+                parsed_free(parsed);
+                if (handler->shut) {
+                    return -1;
+                }
+            }
+            memmove(handler->buf, buf, (handler->end_pos = end - buf));
+        }
+    }
+}
+
+static int handle_outgoing(RedsStreamContext *peer, OutgoingHandler *handler)
+{
+    if (!handler->length) {
+        return 0;
+    }
+
+    while (handler->length) {
+        int n;
+
+        n = peer->cb_write(peer->ctx, handler->now, handler->length);
+        if (n <= 0) {
+            if (n == 0) {
+                return -1;
+            }
+            switch (errno) {
+            case EAGAIN:
+                return 0;
+            case EINTR:
+                break;
+            case EPIPE:
+                return -1;
+            default:
+                red_printf("%s", strerror(errno));
+                return -1;
+            }
+        } else {
+            handler->now += n;
+            handler->length -= n;
+        }
+    }
+    handler->select(handler->opaque, FALSE);
+    handler->may_write(handler->opaque);
+    return 0;
+}
+
+#define OUTGOING_OK 0
+#define OUTGOING_FAILED -1
+#define OUTGOING_BLOCKED 1
+
+static int outgoing_write(RedsStreamContext *peer, OutgoingHandler *handler, void *in_data,
+                          int length)
+{
+    uint8_t *data = in_data;
+    ASSERT(length <= SEND_BUF_SIZE);
+    if (handler->length) {
+        return OUTGOING_BLOCKED;
+    }
+
+    while (length) {
+        int n = peer->cb_write(peer->ctx, data, length);
+        if (n < 0) {
+            switch (errno) {
+            case EAGAIN:
+                handler->length = length;
+                memcpy(handler->buf, data, length);
+                handler->select(handler->opaque, TRUE);
+                return OUTGOING_OK;
+            case EINTR:
+                break;
+            case EPIPE:
+                return OUTGOING_FAILED;
+            default:
+                red_printf("%s", strerror(errno));
+                return OUTGOING_FAILED;
+            }
+        } else {
+            data += n;
+            length -= n;
+        }
+    }
+    return OUTGOING_OK;
+}
+
+#define RED_MOUSE_STATE_TO_LOCAL(state)     \
+    ((state & SPICE_MOUSE_BUTTON_MASK_LEFT) |          \
+     ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) << 1) |   \
+     ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) >> 1))
+
+#define RED_MOUSE_BUTTON_STATE_TO_AGENT(state)                      \
+    (((state & SPICE_MOUSE_BUTTON_MASK_LEFT) ? VD_AGENT_LBUTTON_MASK : 0) |    \
+     ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) ? VD_AGENT_MBUTTON_MASK : 0) |    \
+     ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) ? VD_AGENT_RBUTTON_MASK : 0))
+
+static void activate_modifiers_watch()
+{
+    core->timer_start(key_modifiers_timer, KEY_MODIFIERS_TTL);
+}
+
+static void kbd_push_scan(SpiceKbdInstance *sin, uint8_t scan)
+{
+    SpiceKbdInterface *sif;
+
+    if (!sin) {
+        return;
+    }
+    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceKbdInterface, base);
+    sif->push_scan_freg(sin, scan);
+}
+
+static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
+{
+    SpiceKbdInterface *sif;
+
+    if (!sin) {
+        return 0;
+    }
+    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceKbdInterface, base);
+    return sif->get_leds(sin);
+}
+
+static SpiceMarshaller *marshaller_new_for_outgoing(InputsState *state, int type)
+{
+    SpiceMarshaller *m;
+    SpiceDataHeader *header;
+
+    m = spice_marshaller_new();
+    header = (SpiceDataHeader *)
+        spice_marshaller_reserve_space(m, sizeof(SpiceDataHeader));
+    header->serial = ++state->serial;
+    header->type = type;
+    header->sub_list = 0;
+
+    return m;
+}
+
+static int marshaller_outgoing_write(SpiceMarshaller *m,
+                                     InputsState *state)
+{
+    SpiceDataHeader *header = (SpiceDataHeader *)spice_marshaller_get_ptr(m);
+    uint8_t *data;
+    size_t len;
+    int free_data;
+
+    spice_marshaller_flush(m);
+    header->size = spice_marshaller_get_total_size(m) - sizeof(SpiceDataHeader);
+
+    data = spice_marshaller_linearize(m, 0, &len, &free_data);
+
+    if (outgoing_write(state->peer, &state->out_handler, data, len) != OUTGOING_OK) {
+        return FALSE;
+    }
+
+    if (free_data) {
+        free(data);
+    }
+
+    return TRUE;
+}
+
+
+static void inputs_handle_input(void *opaque, size_t size, uint32_t type, void *message)
+{
+    InputsState *state = (InputsState *)opaque;
+    uint8_t *buf = (uint8_t *)message;
+    SpiceMarshaller *m;
+
+    switch (type) {
+    case SPICE_MSGC_INPUTS_KEY_DOWN: {
+        SpiceMsgcKeyDown *key_up = (SpiceMsgcKeyDown *)buf;
+        if (key_up->code == CAPS_LOCK_SCAN_CODE || key_up->code == NUM_LOCK_SCAN_CODE ||
+            key_up->code == SCROLL_LOCK_SCAN_CODE) {
+            activate_modifiers_watch();
+        }
+    }
+    case SPICE_MSGC_INPUTS_KEY_UP: {
+        SpiceMsgcKeyDown *key_down = (SpiceMsgcKeyDown *)buf;
+        uint8_t *now = (uint8_t *)&key_down->code;
+        uint8_t *end = now + sizeof(key_down->code);
+        for (; now < end && *now; now++) {
+            kbd_push_scan(keyboard, *now);
+        }
+        break;
+    }
+    case SPICE_MSGC_INPUTS_MOUSE_MOTION: {
+        SpiceMsgcMouseMotion *mouse_motion = (SpiceMsgcMouseMotion *)buf;
+
+        if (++state->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
+            m = marshaller_new_for_outgoing(state, SPICE_MSG_INPUTS_MOUSE_MOTION_ACK);
+            if (!marshaller_outgoing_write(m, state)) {
+                red_printf("motion ack failed");
+                reds_disconnect();
+            }
+            spice_marshaller_destroy(m);
+        }
+        if (mouse && reds_get_mouse_mode() == SPICE_MOUSE_MODE_SERVER) {
+            SpiceMouseInterface *sif;
+            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
+            sif->motion(mouse,
+                        mouse_motion->dx, mouse_motion->dy, 0,
+                        RED_MOUSE_STATE_TO_LOCAL(mouse_motion->buttons_state));
+        }
+        break;
+    }
+    case SPICE_MSGC_INPUTS_MOUSE_POSITION: {
+        SpiceMsgcMousePosition *pos = (SpiceMsgcMousePosition *)buf;
+
+        if (++state->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
+            m = marshaller_new_for_outgoing(state, SPICE_MSG_INPUTS_MOUSE_MOTION_ACK);
+            if (!marshaller_outgoing_write(m, state)) {
+                red_printf("position ack failed");
+                reds_disconnect();
+            }
+            spice_marshaller_destroy(m);
+        }
+        if (reds_get_mouse_mode() != SPICE_MOUSE_MODE_CLIENT) {
+            break;
+        }
+        ASSERT((reds_get_agent_mouse() && reds_has_vdagent()) || tablet);
+        if (!reds_get_agent_mouse() || !reds_has_vdagent()) {
+            SpiceTabletInterface *sif;
+            sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
+            sif->position(tablet, pos->x, pos->y, RED_MOUSE_STATE_TO_LOCAL(pos->buttons_state));
+            break;
+        }
+        VDAgentMouseState *mouse_state = &state->mouse_state;
+        mouse_state->x = pos->x;
+        mouse_state->y = pos->y;
+        mouse_state->buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(pos->buttons_state);
+        mouse_state->display_id = pos->display_id;
+        reds_handle_agent_mouse_event(mouse_state);
+        break;
+    }
+    case SPICE_MSGC_INPUTS_MOUSE_PRESS: {
+        SpiceMsgcMousePress *mouse_press = (SpiceMsgcMousePress *)buf;
+        int dz = 0;
+        if (mouse_press->button == SPICE_MOUSE_BUTTON_UP) {
+            dz = -1;
+        } else if (mouse_press->button == SPICE_MOUSE_BUTTON_DOWN) {
+            dz = 1;
+        }
+        if (reds_get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
+            if (reds_get_agent_mouse() && reds_has_vdagent()) {
+                inputs_state->mouse_state.buttons =
+                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_press->buttons_state) |
+                    (dz == -1 ? VD_AGENT_UBUTTON_MASK : 0) |
+                    (dz == 1 ? VD_AGENT_DBUTTON_MASK : 0);
+                reds_handle_agent_mouse_event(&inputs_state->mouse_state);
+            } else if (tablet) {
+                SpiceTabletInterface *sif;
+                sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
+                sif->wheel(tablet, dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
+            }
+        } else if (mouse) {
+            SpiceMouseInterface *sif;
+            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
+            sif->motion(mouse, 0, 0, dz,
+                        RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
+        }
+        break;
+    }
+    case SPICE_MSGC_INPUTS_MOUSE_RELEASE: {
+        SpiceMsgcMouseRelease *mouse_release = (SpiceMsgcMouseRelease *)buf;
+        if (reds_get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
+            if (reds_get_agent_mouse() && reds_has_vdagent()) {
+                inputs_state->mouse_state.buttons =
+                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_release->buttons_state);
+                reds_handle_agent_mouse_event(&inputs_state->mouse_state);
+            } else if (tablet) {
+                SpiceTabletInterface *sif;
+                sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
+                sif->buttons(tablet, RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
+            }
+        } else if (mouse) {
+            SpiceMouseInterface *sif;
+            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
+            sif->buttons(mouse,
+                         RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
+        }
+        break;
+    }
+    case SPICE_MSGC_INPUTS_KEY_MODIFIERS: {
+        SpiceMsgcKeyModifiers *modifiers = (SpiceMsgcKeyModifiers *)buf;
+        uint8_t leds;
+
+        if (!keyboard) {
+            break;
+        }
+        leds = kbd_get_leds(keyboard);
+        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK) !=
+            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK)) {
+            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE);
+            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE | 0x80);
+        }
+        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK) !=
+            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK)) {
+            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE);
+            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE | 0x80);
+        }
+        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK) !=
+            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK)) {
+            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE);
+            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE | 0x80);
+        }
+        activate_modifiers_watch();
+        break;
+    }
+    case SPICE_MSGC_DISCONNECTING:
+        break;
+    default:
+        red_printf("unexpected type %d", type);
+    }
+}
+
+static void inputs_relase_keys(void)
+{
+    kbd_push_scan(keyboard, 0x2a | 0x80); //LSHIFT
+    kbd_push_scan(keyboard, 0x36 | 0x80); //RSHIFT
+    kbd_push_scan(keyboard, 0xe0); kbd_push_scan(keyboard, 0x1d | 0x80); //RCTRL
+    kbd_push_scan(keyboard, 0x1d | 0x80); //LCTRL
+    kbd_push_scan(keyboard, 0xe0); kbd_push_scan(keyboard, 0x38 | 0x80); //RALT
+    kbd_push_scan(keyboard, 0x38 | 0x80); //LALT
+}
+
+static void inputs_event(int fd, int event, void *data)
+{
+    if (data != inputs_state) {
+        return; // shutdown already happened
+    }
+
+    if (event & SPICE_WATCH_EVENT_READ) {
+        if (handle_incoming(inputs_state->peer, &inputs_state->in_handler)) {
+            inputs_relase_keys();
+            core->watch_remove(inputs_state->peer->watch);
+            inputs_state->peer->watch = NULL;
+            if (inputs_state->channel) {
+                inputs_state->channel->data = NULL;
+            }
+            inputs_state->peer->cb_free(inputs_state->peer);
+            free(inputs_state);
+            inputs_state = NULL;
+        }
+    }
+    if (event & SPICE_WATCH_EVENT_WRITE) {
+        if (handle_outgoing(inputs_state->peer, &inputs_state->out_handler)) {
+            reds_disconnect();
+        }
+    }
+}
+
+
+static void inputs_shutdown(Channel *channel)
+{
+    InputsState *state = (InputsState *)channel->data;
+    if (state) {
+        state->in_handler.shut = TRUE;
+        shutdown(state->peer->socket, SHUT_RDWR);
+        channel->data = NULL;
+        state->channel = NULL;
+        inputs_state = NULL;
+    }
+}
+
+static void inputs_migrate(Channel *channel)
+{
+    InputsState *state = (InputsState *)channel->data;
+    SpiceMarshaller *m;
+    SpiceMsgMigrate migrate;
+
+    m = marshaller_new_for_outgoing(state, SPICE_MSG_MIGRATE);
+
+    migrate.flags = 0;
+    spice_marshall_msg_migrate(m, &migrate);
+
+    if (!marshaller_outgoing_write(m, state)) {
+        red_printf("write failed");
+    }
+    spice_marshaller_destroy(m);
+}
+
+static void inputs_select(void *opaque, int select)
+{
+    int eventmask = SPICE_WATCH_EVENT_READ;
+    red_printf("");
+
+    ASSERT(opaque == inputs_state);
+    if (select) {
+        eventmask |= SPICE_WATCH_EVENT_WRITE;
+    }
+    core->watch_update_mask(inputs_state->peer->watch, eventmask);
+}
+
+static void inputs_may_write(void *opaque)
+{
+    red_printf("");
+}
+
+static void inputs_link(Channel *channel, RedsStreamContext *peer, int migration,
+                        int num_common_caps, uint32_t *common_caps, int num_caps,
+                        uint32_t *caps)
+{
+    int delay_val;
+    int flags;
+
+    red_printf("");
+    ASSERT(channel->data == NULL);
+
+    inputs_state = spice_new0(InputsState, 1);
+
+    delay_val = 1;
+    if (setsockopt(peer->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) {
+        red_printf("setsockopt failed, %s", strerror(errno));
+    }
+
+    if ((flags = fcntl(peer->socket, F_GETFL)) == -1 ||
+                                            fcntl(peer->socket, F_SETFL, flags | O_ASYNC) == -1) {
+        red_printf("fcntl failed, %s", strerror(errno));
+    }
+
+    inputs_state->peer = peer;
+    inputs_state->channel = channel;
+    inputs_state->in_handler.parser = spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL);
+    inputs_state->in_handler.opaque = inputs_state;
+    inputs_state->in_handler.handle_message = inputs_handle_input;
+    inputs_state->out_handler.length = 0;
+    inputs_state->out_handler.opaque = inputs_state;
+    inputs_state->out_handler.select = inputs_select;
+    inputs_state->out_handler.may_write = inputs_may_write;
+    channel->data = inputs_state;
+    peer->watch = core->watch_add(peer->socket, SPICE_WATCH_EVENT_READ,
+                                  inputs_event, inputs_state);
+
+    SpiceMarshaller *m;
+    SpiceMsgInputsInit inputs_init;
+    m = marshaller_new_for_outgoing(inputs_state, SPICE_MSG_INPUTS_INIT);
+    inputs_init.keyboard_modifiers = kbd_get_leds(keyboard);
+    spice_marshall_msg_inputs_init(m, &inputs_init);
+    if (!marshaller_outgoing_write(m, inputs_state)) {
+        red_printf("failed to send modifiers state");
+        reds_disconnect();
+    }
+    spice_marshaller_destroy(m);
+}
+
+void inputs_send_keyboard_modifiers(uint8_t modifiers)
+{
+    SpiceMsgInputsKeyModifiers key_modifiers;
+    SpiceMarshaller *m;
+
+    if (!inputs_state) {
+        return;
+    }
+    ASSERT(inputs_state->peer);
+
+    m = marshaller_new_for_outgoing(inputs_state,
+                    SPICE_MSG_INPUTS_KEY_MODIFIERS);
+
+    key_modifiers.modifiers = modifiers;
+    spice_marshall_msg_inputs_key_modifiers(m, &key_modifiers);
+
+    if (!marshaller_outgoing_write(m, inputs_state)) {
+        red_printf("failed to send modifiers state");
+        reds_disconnect();
+    }
+    spice_marshaller_destroy(m);
+}
+
+void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds)
+{
+    inputs_send_keyboard_modifiers(leds);
+}
+
+static void key_modifiers_sender(void *opaque)
+{
+    inputs_send_keyboard_modifiers(kbd_get_leds(keyboard));
+}
+
+void inputs_init(void)
+{
+    Channel *channel;
+
+    channel = spice_new0(Channel, 1);
+    channel->type = SPICE_CHANNEL_INPUTS;
+    channel->link = inputs_link;
+    channel->shutdown = inputs_shutdown;
+    channel->migrate = inputs_migrate;
+    reds_register_channel(channel);
+
+    if (!(key_modifiers_timer = core->timer_add(key_modifiers_sender, NULL))) {
+        red_error("key modifiers timer create failed");
+    }
+}
+
diff --git a/server/inputs_channel.h b/server/inputs_channel.h
new file mode 100644
index 0000000..491c8a5
--- /dev/null
+++ b/server/inputs_channel.h
@@ -0,0 +1,40 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _INPUTS_CHANNEL_H_
+#define _INPUTS_CHANNEL_H_
+
+// Inputs channel, dealing with keyboard, mouse, tablet.
+// This include should only be used by reds.c and inputs_channel.c
+
+#include <stdint.h>
+#include <spice/vd_agent.h>
+
+void inputs_init(void);
+int inputs_inited(void);
+int inputs_has_tablet(void);
+const VDAgentMouseState *inputs_get_mouse_state(void);
+void inputs_send_keyboard_modifiers(uint8_t modifiers);
+void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds);
+int inputs_set_keyboard(SpiceKbdInstance *_keyboard);
+int inputs_set_mouse(SpiceMouseInstance *_mouse);
+int inputs_set_tablet(SpiceTabletInstance *_tablet);
+void inputs_detach_tablet(SpiceTabletInstance *_tablet);
+void inputs_set_tablet_logical_size(int x_res, int y_res);
+
+#endif
+
diff --git a/server/reds.c b/server/reds.c
index 150c6c6..d4223b9 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -45,6 +45,7 @@
 #include <spice/protocol.h>
 #include <spice/vd_agent.h>
 
+#include "inputs_channel.h"
 #include "red_common.h"
 #include "red_dispatcher.h"
 #include "snd_worker.h"
@@ -64,9 +65,6 @@
 #endif
 
 SpiceCoreInterface *core = NULL;
-static SpiceKbdInstance *keyboard = NULL;
-static SpiceMouseInstance *mouse = NULL;
-static SpiceTabletInstance *tablet = NULL;
 static SpiceCharDeviceInstance *vdagent = NULL;
 
 #define MIGRATION_NOTIFY_SPICE_KEY "spice_mig_ext"
@@ -107,21 +105,19 @@ static void openssl_init();
 
 #define MIGRATE_TIMEOUT (1000 * 10) /* 10sec */
 #define PING_INTERVAL (1000 * 10)
-#define KEY_MODIFIERS_TTL (1000 * 2) /*2sec*/
 #define MM_TIMER_GRANULARITY_MS (1000 / 30)
 #define MM_TIME_DELTA 400 /*ms*/
 #define VDI_PORT_WRITE_RETRY_TIMEOUT 100 /*ms*/
 
-// approximate max receive message size
+// approximate max receive message size for main channel
 #define RECEIVE_BUF_SIZE \
     (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
 
-#define SEND_BUF_SIZE 4096
-
 #define SCROLL_LOCK_SCAN_CODE 0x46
 #define NUM_LOCK_SCAN_CODE 0x45
 #define CAPS_LOCK_SCAN_CODE 0x3a
 
+// TODO - remove and use red_channel.h
 typedef struct IncomingHandler {
     spice_parse_channel_func_t parser;
     void *opaque;
@@ -131,14 +127,6 @@ typedef struct IncomingHandler {
     void (*handle_message)(void *opaque, size_t size, uint32_t type, void *message);
 } IncomingHandler;
 
-typedef struct OutgoingHandler {
-    void *opaque;
-    uint8_t buf[SEND_BUF_SIZE];
-    uint8_t *now;
-    uint32_t length;
-    void (*select)(void *opaque, int select);
-    void (*may_write)(void *opaque);
-} OutgoingHandler;
 
 typedef struct TicketAuthentication {
     char password[SPICE_MAX_PASSWORD_LENGTH];
@@ -205,17 +193,6 @@ typedef struct VDIPortState {
     int client_agent_started;
 } VDIPortState;
 
-typedef struct InputsState {
-    Channel *channel;
-    RedsStreamContext *peer;
-    IncomingHandler in_handler;
-    OutgoingHandler out_handler;
-    VDAgentMouseState mouse_state;
-    int pending_mouse_event;
-    uint32_t motion_count;
-    uint64_t serial; //migrate me
-} InputsState;
-
 typedef struct RedsOutgoingData {
     Ring pipe;
     RedsOutItem *item;
@@ -258,7 +235,7 @@ typedef struct RedsState {
     uint32_t link_id;
     uint64_t serial; //migrate me
     VDIPortState agent_state;
-    InputsState *inputs_state;
+    int pending_mouse_event;
 
     int mig_wait_connect;
     int mig_wait_disconnect;
@@ -274,7 +251,6 @@ typedef struct RedsState {
     int dispatcher_allows_client_mouse;
     MonitorMode monitor_mode;
     SpiceTimer *mig_timer;
-    SpiceTimer *key_modifiers_timer;
     SpiceTimer *mm_timer;
     SpiceTimer *vdi_port_write_timer;
     int vdi_port_write_timer_started;
@@ -727,7 +703,7 @@ static void reds_reset_outgoing()
     outgoing->vec = outgoing->vec_buf;
 }
 
-static void reds_disconnect()
+void reds_disconnect()
 {
     if (!reds->peer || reds->disconnecting) {
         return;
@@ -829,79 +805,6 @@ static int handle_incoming(RedsStreamContext *peer, IncomingHandler *handler)
     }
 }
 
-static int handle_outgoing(RedsStreamContext *peer, OutgoingHandler *handler)
-{
-    if (!handler->length) {
-        return 0;
-    }
-
-    while (handler->length) {
-        int n;
-
-        n = peer->cb_write(peer->ctx, handler->now, handler->length);
-        if (n <= 0) {
-            if (n == 0) {
-                return -1;
-            }
-            switch (errno) {
-            case EAGAIN:
-                return 0;
-            case EINTR:
-                break;
-            case EPIPE:
-                return -1;
-            default:
-                red_printf("%s", strerror(errno));
-                return -1;
-            }
-        } else {
-            handler->now += n;
-            handler->length -= n;
-        }
-    }
-    handler->select(handler->opaque, FALSE);
-    handler->may_write(handler->opaque);
-    return 0;
-}
-
-#define OUTGOING_OK 0
-#define OUTGOING_FAILED -1
-#define OUTGOING_BLOCKED 1
-
-static int outgoing_write(RedsStreamContext *peer, OutgoingHandler *handler, void *in_data,
-                          int length)
-{
-    uint8_t *data = in_data;
-    ASSERT(length <= SEND_BUF_SIZE);
-    if (handler->length) {
-        return OUTGOING_BLOCKED;
-    }
-
-    while (length) {
-        int n = peer->cb_write(peer->ctx, data, length);
-        if (n < 0) {
-            switch (errno) {
-            case EAGAIN:
-                handler->length = length;
-                memcpy(handler->buf, data, length);
-                handler->select(handler->opaque, TRUE);
-                return OUTGOING_OK;
-            case EINTR:
-                break;
-            case EPIPE:
-                return OUTGOING_FAILED;
-            default:
-                red_printf("%s", strerror(errno));
-                return OUTGOING_FAILED;
-            }
-        } else {
-            data += n;
-            length -= n;
-        }
-    }
-    return OUTGOING_OK;
-}
-
 static RedsOutItem *new_out_item(uint32_t type)
 {
     RedsOutItem *item;
@@ -1039,6 +942,11 @@ static void reds_send_mouse_mode()
     reds_push_pipe_item(item);
 }
 
+int reds_get_mouse_mode(void)
+{
+    return reds->mouse_mode;
+}
+
 static void reds_set_mouse_mode(uint32_t mode)
 {
     if (reds->mouse_mode == mode) {
@@ -1049,12 +957,17 @@ static void reds_set_mouse_mode(uint32_t mode)
     reds_send_mouse_mode();
 }
 
+int reds_get_agent_mouse(void)
+{
+    return agent_mouse;
+}
+
 static void reds_update_mouse_mode()
 {
     int allowed = 0;
     int qxl_count = red_dispatcher_qxl_count();
 
-    if ((agent_mouse && vdagent) || (tablet && qxl_count == 1)) {
+    if ((agent_mouse && vdagent) || (inputs_has_tablet() && qxl_count == 1)) {
         allowed = reds->dispatcher_allows_client_mouse;
     }
     if (allowed == reds->is_client_mouse_allowed) {
@@ -1334,26 +1247,31 @@ void vdagent_char_device_wakeup(SpiceCharDeviceInstance *sin)
     while (read_from_vdi_port());
 }
 
-static void reds_handle_agent_mouse_event()
+int reds_has_vdagent(void)
+{
+    return !!vdagent;
+}
+
+void reds_handle_agent_mouse_event(const VDAgentMouseState *mouse_state)
 {
     RingItem *ring_item;
     VDInternalBuf *buf;
 
-    if (!reds->inputs_state) {
+    if (!inputs_inited()) {
         return;
     }
     if (reds->mig_target || !(ring_item = ring_get_head(&reds->agent_state.internal_bufs))) {
-        reds->inputs_state->pending_mouse_event = TRUE;
+        reds->pending_mouse_event = TRUE;
         vdi_port_write_timer_start();
         return;
     }
-    reds->inputs_state->pending_mouse_event = FALSE;
+    reds->pending_mouse_event = FALSE;
     ring_remove(ring_item);
     buf = (VDInternalBuf *)ring_item;
     buf->base.now = (uint8_t *)&buf->base.chunk_header;
     buf->base.write_len = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) +
                           sizeof(VDAgentMouseState);
-    buf->u.mouse_state = reds->inputs_state->mouse_state;
+    buf->u.mouse_state = *mouse_state;
     ring_add(&reds->agent_state.write_queue, &buf->base.link);
     write_to_vdi_port();
 }
@@ -2098,398 +2016,15 @@ static void reds_handle_main_link(RedLinkInfo *link)
      ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) ? VD_AGENT_MBUTTON_MASK : 0) |    \
      ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) ? VD_AGENT_RBUTTON_MASK : 0))
 
-static void activate_modifiers_watch()
-{
-    core->timer_start(reds->key_modifiers_timer, KEY_MODIFIERS_TTL);
-}
-
-static void kbd_push_scan(SpiceKbdInstance *sin, uint8_t scan)
-{
-    SpiceKbdInterface *sif;
-
-    if (!sin) {
-        return;
-    }
-    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceKbdInterface, base);
-    sif->push_scan_freg(sin, scan);
-}
-
-static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
-{
-    SpiceKbdInterface *sif;
-
-    if (!sin) {
-        return 0;
-    }
-    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceKbdInterface, base);
-    return sif->get_leds(sin);
-}
-
-static SpiceMarshaller *marshaller_new_for_outgoing(InputsState *state, int type)
-{
-    SpiceMarshaller *m;
-    SpiceDataHeader *header;
-
-    m = spice_marshaller_new();
-    header = (SpiceDataHeader *)
-        spice_marshaller_reserve_space(m, sizeof(SpiceDataHeader));
-    header->serial = ++state->serial;
-    header->type = type;
-    header->sub_list = 0;
-
-    return m;
-}
-
-static int marshaller_outgoing_write(SpiceMarshaller *m,
-                                     InputsState *state)
-{
-    SpiceDataHeader *header = (SpiceDataHeader *)spice_marshaller_get_ptr(m);
-    uint8_t *data;
-    size_t len;
-    int free_data;
-
-    spice_marshaller_flush(m);
-    header->size = spice_marshaller_get_total_size(m) - sizeof(SpiceDataHeader);
-
-    data = spice_marshaller_linearize(m, 0, &len, &free_data);
-
-    if (outgoing_write(state->peer, &state->out_handler, data, len) != OUTGOING_OK) {
-        return FALSE;
-    }
-
-    if (free_data) {
-        free(data);
-    }
-
-    return TRUE;
-}
-
-
-static void inputs_handle_input(void *opaque, size_t size, uint32_t type, void *message)
-{
-    InputsState *state = (InputsState *)opaque;
-    uint8_t *buf = (uint8_t *)message;
-    SpiceMarshaller *m;
-
-    switch (type) {
-    case SPICE_MSGC_INPUTS_KEY_DOWN: {
-        SpiceMsgcKeyDown *key_up = (SpiceMsgcKeyDown *)buf;
-        if (key_up->code == CAPS_LOCK_SCAN_CODE || key_up->code == NUM_LOCK_SCAN_CODE ||
-            key_up->code == SCROLL_LOCK_SCAN_CODE) {
-            activate_modifiers_watch();
-        }
-    }
-    case SPICE_MSGC_INPUTS_KEY_UP: {
-        SpiceMsgcKeyDown *key_down = (SpiceMsgcKeyDown *)buf;
-        uint8_t *now = (uint8_t *)&key_down->code;
-        uint8_t *end = now + sizeof(key_down->code);
-        for (; now < end && *now; now++) {
-            kbd_push_scan(keyboard, *now);
-        }
-        break;
-    }
-    case SPICE_MSGC_INPUTS_MOUSE_MOTION: {
-        SpiceMsgcMouseMotion *mouse_motion = (SpiceMsgcMouseMotion *)buf;
-
-        if (++state->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
-            m = marshaller_new_for_outgoing(state, SPICE_MSG_INPUTS_MOUSE_MOTION_ACK);
-            if (!marshaller_outgoing_write(m, state)) {
-                red_printf("motion ack failed");
-                reds_disconnect();
-            }
-            spice_marshaller_destroy(m);
-        }
-        if (mouse && reds->mouse_mode == SPICE_MOUSE_MODE_SERVER) {
-            SpiceMouseInterface *sif;
-            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
-            sif->motion(mouse,
-                        mouse_motion->dx, mouse_motion->dy, 0,
-                        RED_MOUSE_STATE_TO_LOCAL(mouse_motion->buttons_state));
-        }
-        break;
-    }
-    case SPICE_MSGC_INPUTS_MOUSE_POSITION: {
-        SpiceMsgcMousePosition *pos = (SpiceMsgcMousePosition *)buf;
-
-        if (++state->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0) {
-            m = marshaller_new_for_outgoing(state, SPICE_MSG_INPUTS_MOUSE_MOTION_ACK);
-            if (!marshaller_outgoing_write(m, state)) {
-                red_printf("position ack failed");
-                reds_disconnect();
-            }
-            spice_marshaller_destroy(m);
-        }
-        if (reds->mouse_mode != SPICE_MOUSE_MODE_CLIENT) {
-            break;
-        }
-        ASSERT((agent_mouse && vdagent) || tablet);
-        if (!agent_mouse || !vdagent) {
-            SpiceTabletInterface *sif;
-            sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
-            sif->position(tablet, pos->x, pos->y, RED_MOUSE_STATE_TO_LOCAL(pos->buttons_state));
-            break;
-        }
-        VDAgentMouseState *mouse_state = &state->mouse_state;
-        mouse_state->x = pos->x;
-        mouse_state->y = pos->y;
-        mouse_state->buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(pos->buttons_state);
-        mouse_state->display_id = pos->display_id;
-        reds_handle_agent_mouse_event();
-        break;
-    }
-    case SPICE_MSGC_INPUTS_MOUSE_PRESS: {
-        SpiceMsgcMousePress *mouse_press = (SpiceMsgcMousePress *)buf;
-        int dz = 0;
-        if (mouse_press->button == SPICE_MOUSE_BUTTON_UP) {
-            dz = -1;
-        } else if (mouse_press->button == SPICE_MOUSE_BUTTON_DOWN) {
-            dz = 1;
-        }
-        if (reds->mouse_mode == SPICE_MOUSE_MODE_CLIENT) {
-            if (agent_mouse && vdagent) {
-                reds->inputs_state->mouse_state.buttons =
-                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_press->buttons_state) |
-                    (dz == -1 ? VD_AGENT_UBUTTON_MASK : 0) |
-                    (dz == 1 ? VD_AGENT_DBUTTON_MASK : 0);
-                reds_handle_agent_mouse_event();
-            } else if (tablet) {
-                SpiceTabletInterface *sif;
-                sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
-                sif->wheel(tablet, dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
-            }
-        } else if (mouse) {
-            SpiceMouseInterface *sif;
-            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
-            sif->motion(mouse, 0, 0, dz,
-                        RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
-        }
-        break;
-    }
-    case SPICE_MSGC_INPUTS_MOUSE_RELEASE: {
-        SpiceMsgcMouseRelease *mouse_release = (SpiceMsgcMouseRelease *)buf;
-        if (reds->mouse_mode == SPICE_MOUSE_MODE_CLIENT) {
-            if (agent_mouse && vdagent) {
-                reds->inputs_state->mouse_state.buttons =
-                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_release->buttons_state);
-                reds_handle_agent_mouse_event();
-            } else if (tablet) {
-                SpiceTabletInterface *sif;
-                sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
-                sif->buttons(tablet, RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
-            }
-        } else if (mouse) {
-            SpiceMouseInterface *sif;
-            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
-            sif->buttons(mouse,
-                         RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
-        }
-        break;
-    }
-    case SPICE_MSGC_INPUTS_KEY_MODIFIERS: {
-        SpiceMsgcKeyModifiers *modifiers = (SpiceMsgcKeyModifiers *)buf;
-        uint8_t leds;
-
-        if (!keyboard) {
-            break;
-        }
-        leds = kbd_get_leds(keyboard);
-        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK) !=
-            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK)) {
-            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE);
-            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE | 0x80);
-        }
-        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK) !=
-            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK)) {
-            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE);
-            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE | 0x80);
-        }
-        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK) !=
-            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK)) {
-            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE);
-            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE | 0x80);
-        }
-        activate_modifiers_watch();
-        break;
-    }
-    case SPICE_MSGC_DISCONNECTING:
-        break;
-    default:
-        red_printf("unexpected type %d", type);
-    }
-}
-
 void reds_set_client_mouse_allowed(int is_client_mouse_allowed, int x_res, int y_res)
 {
     reds->monitor_mode.x_res = x_res;
     reds->monitor_mode.y_res = y_res;
     reds->dispatcher_allows_client_mouse = is_client_mouse_allowed;
     reds_update_mouse_mode();
-    if (reds->is_client_mouse_allowed && tablet) {
-        SpiceTabletInterface *sif;
-        sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
-        sif->set_logical_size(tablet, reds->monitor_mode.x_res, reds->monitor_mode.y_res);
-    }
-}
-
-static void inputs_relase_keys(void)
-{
-    kbd_push_scan(keyboard, 0x2a | 0x80); //LSHIFT
-    kbd_push_scan(keyboard, 0x36 | 0x80); //RSHIFT
-    kbd_push_scan(keyboard, 0xe0); kbd_push_scan(keyboard, 0x1d | 0x80); //RCTRL
-    kbd_push_scan(keyboard, 0x1d | 0x80); //LCTRL
-    kbd_push_scan(keyboard, 0xe0); kbd_push_scan(keyboard, 0x38 | 0x80); //RALT
-    kbd_push_scan(keyboard, 0x38 | 0x80); //LALT
-}
-
-static void inputs_event(int fd, int event, void *data)
-{
-    InputsState *inputs_state = data;
-
-    if (event & SPICE_WATCH_EVENT_READ) {
-        if (handle_incoming(inputs_state->peer, &inputs_state->in_handler)) {
-            inputs_relase_keys();
-            core->watch_remove(inputs_state->peer->watch);
-            inputs_state->peer->watch = NULL;
-            if (inputs_state->channel) {
-                inputs_state->channel->data = NULL;
-                reds->inputs_state = NULL;
-            }
-            inputs_state->peer->cb_free(inputs_state->peer);
-            free(inputs_state);
-        }
-    }
-    if (event & SPICE_WATCH_EVENT_WRITE) {
-        if (handle_outgoing(inputs_state->peer, &inputs_state->out_handler)) {
-            reds_disconnect();
-        }
-    }
-}
-
-
-static void inputs_shutdown(Channel *channel)
-{
-    InputsState *state = (InputsState *)channel->data;
-    if (state) {
-        state->in_handler.shut = TRUE;
-        shutdown(state->peer->socket, SHUT_RDWR);
-        channel->data = NULL;
-        state->channel = NULL;
-        reds->inputs_state = NULL;
-    }
-}
-
-static void inputs_migrate(Channel *channel)
-{
-    InputsState *state = (InputsState *)channel->data;
-    SpiceMarshaller *m;
-    SpiceMsgMigrate migrate;
-
-    m = marshaller_new_for_outgoing(state, SPICE_MSG_MIGRATE);
-
-    migrate.flags = 0;
-    spice_marshall_msg_migrate(m, &migrate);
-
-    if (!marshaller_outgoing_write(m, state)) {
-        red_printf("write failed");
-    }
-    spice_marshaller_destroy(m);
-}
-
-static void inputs_select(void *opaque, int select)
-{
-    InputsState *inputs_state;
-    int eventmask = SPICE_WATCH_EVENT_READ;
-    red_printf("");
-
-    inputs_state = (InputsState *)opaque;
-    if (select) {
-        eventmask |= SPICE_WATCH_EVENT_WRITE;
-    }
-    core->watch_update_mask(inputs_state->peer->watch, eventmask);
-}
-
-static void inputs_may_write(void *opaque)
-{
-    red_printf("");
-}
-
-static void inputs_link(Channel *channel, RedsStreamContext *peer, int migration,
-                        int num_common_caps, uint32_t *common_caps, int num_caps,
-                        uint32_t *caps)
-{
-    InputsState *inputs_state;
-    int delay_val;
-    int flags;
-
-    red_printf("");
-    ASSERT(channel->data == NULL);
-
-    inputs_state = spice_new0(InputsState, 1);
-
-    delay_val = 1;
-    if (setsockopt(peer->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) {
-        red_printf("setsockopt failed, %s", strerror(errno));
-    }
-
-    if ((flags = fcntl(peer->socket, F_GETFL)) == -1 ||
-                                            fcntl(peer->socket, F_SETFL, flags | O_ASYNC) == -1) {
-        red_printf("fcntl failed, %s", strerror(errno));
+    if (reds->is_client_mouse_allowed && inputs_has_tablet()) {
+        inputs_set_tablet_logical_size(reds->monitor_mode.x_res, reds->monitor_mode.y_res);
     }
-
-    inputs_state->peer = peer;
-    inputs_state->channel = channel;
-    inputs_state->in_handler.parser = spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL);
-    inputs_state->in_handler.opaque = inputs_state;
-    inputs_state->in_handler.handle_message = inputs_handle_input;
-    inputs_state->out_handler.length = 0;
-    inputs_state->out_handler.opaque = inputs_state;
-    inputs_state->out_handler.select = inputs_select;
-    inputs_state->out_handler.may_write = inputs_may_write;
-    inputs_state->pending_mouse_event = FALSE;
-    channel->data = inputs_state;
-    reds->inputs_state = inputs_state;
-    peer->watch = core->watch_add(peer->socket, SPICE_WATCH_EVENT_READ,
-                                  inputs_event, inputs_state);
-
-    SpiceMarshaller *m;
-    SpiceMsgInputsInit inputs_init;
-    m = marshaller_new_for_outgoing(inputs_state, SPICE_MSG_INPUTS_INIT);
-    inputs_init.keyboard_modifiers = kbd_get_leds(keyboard);
-    spice_marshall_msg_inputs_init(m, &inputs_init);
-    if (!marshaller_outgoing_write(m, inputs_state)) {
-        red_printf("failed to send modifiers state");
-        reds_disconnect();
-    }
-    spice_marshaller_destroy(m);
-}
-
-static void reds_send_keyboard_modifiers(uint8_t modifiers)
-{
-    Channel *channel = reds_find_channel(SPICE_CHANNEL_INPUTS, 0);
-    InputsState *state;
-    SpiceMsgInputsKeyModifiers key_modifiers;
-    SpiceMarshaller *m;
-
-    if (!channel || !(state = (InputsState *)channel->data)) {
-        return;
-    }
-    ASSERT(state->peer);
-
-    m = marshaller_new_for_outgoing(state, SPICE_MSG_INPUTS_KEY_MODIFIERS);
-
-    key_modifiers.modifiers = modifiers;
-    spice_marshall_msg_inputs_key_modifiers(m, &key_modifiers);
-
-    if (!marshaller_outgoing_write(m, state)) {
-        red_printf("failed to send modifiers state");
-        reds_disconnect();
-    }
-    spice_marshaller_destroy(m);
-}
-
-static void reds_on_keyboard_leds_change(void *opaque, uint8_t leds)
-{
-    reds_send_keyboard_modifiers(leds);
 }
 
 static void openssl_init(RedLinkInfo *link)
@@ -2504,18 +2039,6 @@ static void openssl_init(RedLinkInfo *link)
     BN_set_word(link->tiTicketing.bn, f4);
 }
 
-static void inputs_init()
-{
-    Channel *channel;
-
-    channel = spice_new0(Channel, 1);
-    channel->type = SPICE_CHANNEL_INPUTS;
-    channel->link = inputs_link;
-    channel->shutdown = inputs_shutdown;
-    channel->migrate = inputs_migrate;
-    reds_register_channel(channel);
-}
-
 static void reds_handle_other_links(RedLinkInfo *link)
 {
     Channel *channel;
@@ -3362,11 +2885,6 @@ static void migrate_timout(void *opaque)
     reds_mig_disconnect();
 }
 
-static void key_modifiers_sender(void *opaque)
-{
-    reds_send_keyboard_modifiers(kbd_get_leds(keyboard));
-}
-
 uint32_t reds_get_mm_time()
 {
     struct timespec time_space;
@@ -3505,32 +3023,24 @@ __visible__ int spice_server_add_interface(SpiceServer *s,
 
     if (strcmp(interface->type, SPICE_INTERFACE_KEYBOARD) == 0) {
         red_printf("SPICE_INTERFACE_KEYBOARD");
-        if (keyboard) {
-            red_printf("already have keyboard");
-            return -1;
-        }
         if (interface->major_version != SPICE_INTERFACE_KEYBOARD_MAJOR ||
             interface->minor_version < SPICE_INTERFACE_KEYBOARD_MINOR) {
             red_printf("unsupported keyboard interface");
             return -1;
         }
-        keyboard = SPICE_CONTAINEROF(sin, SpiceKbdInstance, base);
-        keyboard->st = spice_new0(SpiceKbdState, 1);
-
-    } else if (strcmp(interface->type, SPICE_INTERFACE_MOUSE) == 0) {
-        red_printf("SPICE_INTERFACE_MOUSE");
-        if (mouse) {
-            red_printf("already have mouse");
+        if (inputs_set_keyboard(SPICE_CONTAINEROF(sin, SpiceKbdInstance, base)) != 0) {
             return -1;
         }
+    } else if (strcmp(interface->type, SPICE_INTERFACE_MOUSE) == 0) {
+        red_printf("SPICE_INTERFACE_MOUSE");
         if (interface->major_version != SPICE_INTERFACE_MOUSE_MAJOR ||
             interface->minor_version < SPICE_INTERFACE_MOUSE_MINOR) {
             red_printf("unsupported mouse interface");
             return -1;
         }
-        mouse = SPICE_CONTAINEROF(sin, SpiceMouseInstance, base);
-        mouse->st = spice_new0(SpiceMouseState, 1);
-
+        if (inputs_set_mouse(SPICE_CONTAINEROF(sin, SpiceMouseInstance, base)) != 0) {
+            return -1;
+        }
     } else if (strcmp(interface->type, SPICE_INTERFACE_QXL) == 0) {
         QXLInstance *qxl;
 
@@ -3548,23 +3058,17 @@ __visible__ int spice_server_add_interface(SpiceServer *s,
 
     } else if (strcmp(interface->type, SPICE_INTERFACE_TABLET) == 0) {
         red_printf("SPICE_INTERFACE_TABLET");
-        if (tablet) {
-            red_printf("already have tablet");
-            return -1;
-        }
         if (interface->major_version != SPICE_INTERFACE_TABLET_MAJOR ||
             interface->minor_version < SPICE_INTERFACE_TABLET_MINOR) {
             red_printf("unsupported tablet interface");
             return -1;
         }
-        tablet = SPICE_CONTAINEROF(sin, SpiceTabletInstance, base);
-        tablet->st = spice_new0(SpiceTabletState, 1);
+        if (inputs_set_tablet(SPICE_CONTAINEROF(sin, SpiceTabletInstance, base)) != 0) {
+            return -1;
+        }
         reds_update_mouse_mode();
         if (reds->is_client_mouse_allowed) {
-            SpiceTabletInterface *sif;
-            sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
-            sif->set_logical_size(tablet, reds->monitor_mode.x_res,
-                                  reds->monitor_mode.y_res);
+            inputs_set_tablet_logical_size(reds->monitor_mode.x_res, reds->monitor_mode.y_res);
         }
 
     } else if (strcmp(interface->type, SPICE_INTERFACE_PLAYBACK) == 0) {
@@ -3624,11 +3128,8 @@ __visible__ int spice_server_remove_interface(SpiceBaseInstance *sin)
 
     if (strcmp(interface->type, SPICE_INTERFACE_TABLET) == 0) {
         red_printf("remove SPICE_INTERFACE_TABLET");
-        if (sin == &tablet->base) {
-            tablet = NULL;
-            reds_update_mouse_mode();
-        }
-
+        inputs_detach_tablet(SPICE_CONTAINEROF(sin, SpiceTabletInstance, base));
+        reds_update_mouse_mode();
     } else if (strcmp(interface->type, SPICE_INTERFACE_PLAYBACK) == 0) {
         red_printf("remove SPICE_INTERFACE_PLAYBACK");
         snd_detach_playback(SPICE_CONTAINEROF(sin, SpicePlaybackInstance, base));
@@ -3660,8 +3161,8 @@ static void free_internal_agent_buff(VDIPortBuf *in_buf)
     VDIPortState *state = &reds->agent_state;
 
     ring_add(&state->internal_bufs, &in_buf->link);
-    if (reds->inputs_state && reds->inputs_state->pending_mouse_event) {
-        reds_handle_agent_mouse_event();
+    if (inputs_inited() && reds->pending_mouse_event) {
+        reds_handle_agent_mouse_event(inputs_get_mouse_state());
     }
 }
 
@@ -3730,9 +3231,6 @@ static void do_spice_init(SpiceCoreInterface *core_interface)
     if (!(reds->mig_timer = core->timer_add(migrate_timout, NULL))) {
         red_error("migration timer create failed");
     }
-    if (!(reds->key_modifiers_timer = core->timer_add(key_modifiers_sender, NULL))) {
-        red_error("key modifiers timer create failed");
-    }
     if (!(reds->vdi_port_write_timer = core->timer_add(vdi_port_write_retry, NULL)))
     {
         red_error("vdi port write timer create failed");
@@ -4036,7 +3534,7 @@ __visible__ int spice_server_add_renderer(SpiceServer *s, const char *name)
 
 __visible__ int spice_server_kbd_leds(SpiceKbdInstance *sin, int leds)
 {
-    reds_on_keyboard_leds_change(NULL, leds);
+    inputs_on_keyboard_leds_change(NULL, leds);
     return 0;
 }
 
diff --git a/server/reds.h b/server/reds.h
index e95aea5..7f656e3 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -21,6 +21,7 @@
 #include <stdint.h>
 #include <openssl/ssl.h>
 #include <sys/uio.h>
+#include <spice/vd_agent.h>
 
 #define __visible__ __attribute__ ((visibility ("default")))
 
@@ -90,11 +91,18 @@ void reds_set_client_mouse_allowed(int is_client_mouse_allowed,
                                    int x_res, int y_res);
 void reds_register_channel(Channel *channel);
 void reds_unregister_channel(Channel *channel);
+int reds_get_mouse_mode(void); // used by inputs_channel
+int reds_get_agent_mouse(void); // used by inputs_channel
+int reds_has_vdagent(void); // used by inputs channel
+void reds_handle_agent_mouse_event(const VDAgentMouseState *mouse_state); // used by inputs_channel
 
 extern struct SpiceCoreInterface *core;
 extern uint64_t bitrate_per_sec;
 
 #define IS_LOW_BANDWIDTH() (bitrate_per_sec < 10 * 1024 * 1024)
 
+// Temporary measures to make splitting reds.c to inputs_channel.c easier
+void reds_disconnect(void);
+
 #endif
 
commit 26c40c4196a6c1b12dc59a794d64fb217b5a7259
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Oct 31 16:28:55 2010 +0200

    server/reds: fix typo

diff --git a/server/reds.c b/server/reds.c
index b4ec6e1..150c6c6 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -79,7 +79,7 @@ static SpiceCharDeviceInstance *vdagent = NULL;
 #define REDS_AGENT_WINDOW_SIZE 10
 #define REDS_TOKENS_TO_SEND 5
 #define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
-#define REDS_VDI_PORT_NUM_RECIVE_BUFFS 5
+#define REDS_VDI_PORT_NUM_RECEIVE_BUFFS 5
 #define REDS_MAX_SEND_IOVEC 100
 
 #define NET_TEST_WARMUP_BYTES 0
@@ -113,7 +113,7 @@ static void openssl_init();
 #define VDI_PORT_WRITE_RETRY_TIMEOUT 100 /*ms*/
 
 // approximate max receive message size
-#define RECIVE_BUF_SIZE \
+#define RECEIVE_BUF_SIZE \
     (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
 
 #define SEND_BUF_SIZE 4096
@@ -126,7 +126,7 @@ typedef struct IncomingHandler {
     spice_parse_channel_func_t parser;
     void *opaque;
     int shut;
-    uint8_t buf[RECIVE_BUF_SIZE];
+    uint8_t buf[RECEIVE_BUF_SIZE];
     uint32_t end_pos;
     void (*handle_message)(void *opaque, size_t size, uint32_t type, void *message);
 } IncomingHandler;
@@ -784,7 +784,7 @@ static int handle_incoming(RedsStreamContext *peer, IncomingHandler *handler)
         uint8_t *end = buf + pos;
         SpiceDataHeader *header;
         int n;
-        n = peer->cb_read(peer->ctx, buf + pos, RECIVE_BUF_SIZE - pos);
+        n = peer->cb_read(peer->ctx, buf + pos, RECEIVE_BUF_SIZE - pos);
         if (n <= 0) {
             if (n == 0) {
                 return -1;
@@ -3700,7 +3700,7 @@ static void init_vd_agent_resources()
         ring_add(&reds->agent_state.internal_bufs, &buf->base.link);
     }
 
-    for (i = 0; i < REDS_VDI_PORT_NUM_RECIVE_BUFFS; i++) {
+    for (i = 0; i < REDS_VDI_PORT_NUM_RECEIVE_BUFFS; i++) {
         VDIReadBuf *buf = spice_new0(VDIReadBuf, 1);
         ring_item_init(&buf->link);
         ring_add(&reds->agent_state.read_bufs, &buf->link);


More information about the Spice-commits mailing list