diff options
author | Alon Levy <alevy@redhat.com> | 2010-10-31 16:31:14 +0200 |
---|---|---|
committer | Alon Levy <alevy@redhat.com> | 2010-12-07 21:32:33 +0200 |
commit | 347e32177cd7a563054ac903e1efb193f6ec7b57 (patch) | |
tree | 325974ac49ac33cdf9e127a852cdfe78da31e1e3 | |
parent | 26c40c4196a6c1b12dc59a794d64fb217b5a7259 (diff) | |
download | spice-347e32177cd7a563054ac903e1efb193f6ec7b57.tar.gz spice-347e32177cd7a563054ac903e1efb193f6ec7b57.tar.xz spice-347e32177cd7a563054ac903e1efb193f6ec7b57.zip |
server: introduce inputs_channel, split from reds.c
-rw-r--r-- | server/Makefile.am | 1 | ||||
-rw-r--r-- | server/inputs_channel.c | 694 | ||||
-rw-r--r-- | server/inputs_channel.h | 40 | ||||
-rw-r--r-- | server/reds.c | 588 | ||||
-rw-r--r-- | server/reds.h | 8 |
5 files changed, 786 insertions, 545 deletions
diff --git a/server/Makefile.am b/server/Makefile.am index 2c86a2c0..ab66ba72 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 00000000..288724e6 --- /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 00000000..491c8a5b --- /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 150c6c65..d4223b9a 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 e95aea5e..7f656e3b 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 |