[Spice-devel] [PATCH 02/12] server: introduce inputs_channel, split from reds.c

Hans de Goede hdegoede at redhat.com
Mon Dec 6 03:11:04 PST 2010


Ack.

On 12/06/2010 11:49 AM, Alon Levy wrote:
> ---
>   server/Makefile.am      |    1 +
>   server/inputs_channel.c |  694 +++++++++++++++++++++++++++++++++++++++++++++++
>   server/inputs_channel.h |   40 +++
>   server/reds.c           |  588 +++-------------------------------------
>   server/reds.h           |    8 +
>   5 files changed, 786 insertions(+), 545 deletions(-)
>   create mode 100644 server/inputs_channel.c
>   create mode 100644 server/inputs_channel.h
>
> diff --git a/server/Makefile.am b/server/Makefile.am
> index ff7b485..a7bbe15 100644
> --- a/server/Makefile.am
> +++ b/server/Makefile.am
> @@ -104,6 +104,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 d77b9d9..4a8f5ee 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"
> @@ -60,9 +61,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"
> @@ -103,21 +101,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;
> @@ -127,14 +123,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) {
> @@ -1326,26 +1239,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();
>   }
> @@ -2090,400 +2008,17 @@ 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);
> +    if (reds->is_client_mouse_allowed&&  inputs_has_tablet()) {
> +        inputs_set_tablet_logical_size(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));
> -    }
> -
> -    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)
>   {
>       unsigned long f4 = RSA_F4;
> @@ -2496,18 +2031,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;
> @@ -3354,11 +2877,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;
> @@ -3467,32 +2985,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;
>
> @@ -3510,23 +3020,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) {
> @@ -3587,11 +3091,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));
> @@ -3627,8 +3128,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());
>       }
>   }
>
> @@ -3697,9 +3198,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");
> @@ -3999,7 +3497,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
>


More information about the Spice-devel mailing list