mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-26 14:41:25 +00:00
When a message has been read from the network we now pass it into the generated demarshaller for the channel. The demarshaller converts the network data to in-memory structures that is passed on to the spice internals. Additionally it also: * Converts endianness * Validates sizes of message and any pointers in it * Localizes offsets (converts them to pointers) * Checks for zero offsets in messages where they are not supported Some of this was previously done using custom code in the client, this is now removed.
581 lines
17 KiB
C++
581 lines
17 KiB
C++
/*
|
|
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 "common.h"
|
|
#include "inputs_channel.h"
|
|
#include "utils.h"
|
|
#include "debug.h"
|
|
#include "red_client.h"
|
|
#include "application.h"
|
|
#include "display_channel.h"
|
|
|
|
#define SYNC_REMOTH_MODIFIRES
|
|
|
|
class SetInputsHandlerEvent: public Event {
|
|
public:
|
|
SetInputsHandlerEvent(InputsChannel& channel) : _channel (channel) {}
|
|
|
|
class AttachFunc: public ForEachChannelFunc {
|
|
public:
|
|
AttachFunc(InputsChannel& channel)
|
|
: _channel (channel)
|
|
{
|
|
}
|
|
|
|
virtual bool operator() (RedChannel& channel)
|
|
{
|
|
if (channel.get_type() == SPICE_CHANNEL_DISPLAY) {
|
|
static_cast<DisplayChannel&>(channel).attach_inputs(&_channel);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public:
|
|
InputsChannel& _channel;
|
|
};
|
|
|
|
virtual void response(AbstractProcessLoop& events_loop)
|
|
{
|
|
static_cast<Application*>(events_loop.get_owner())->set_key_handler(_channel);
|
|
static_cast<Application*>(events_loop.get_owner())->set_mouse_handler(_channel);
|
|
AttachFunc func(_channel);
|
|
_channel.get_client().for_each_channel(func);
|
|
}
|
|
|
|
private:
|
|
InputsChannel& _channel;
|
|
};
|
|
|
|
class KeyModifiersEvent: public Event {
|
|
public:
|
|
KeyModifiersEvent(InputsChannel& channel) : _channel (channel) {}
|
|
|
|
virtual void response(AbstractProcessLoop& events_loop)
|
|
{
|
|
Lock lock(_channel._update_modifiers_lock);
|
|
_channel._active_modifiers_event = false;
|
|
_channel.set_local_modifiers();
|
|
}
|
|
|
|
private:
|
|
InputsChannel& _channel;
|
|
};
|
|
|
|
class RemoveInputsHandlerEvent: public SyncEvent {
|
|
public:
|
|
RemoveInputsHandlerEvent(InputsChannel& channel) : _channel (channel) {}
|
|
|
|
class DetachFunc: public ForEachChannelFunc {
|
|
public:
|
|
virtual bool operator() (RedChannel& channel)
|
|
{
|
|
if (channel.get_type() == SPICE_CHANNEL_DISPLAY) {
|
|
static_cast<DisplayChannel&>(channel).detach_inputs();
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
virtual void do_response(AbstractProcessLoop& events_loop)
|
|
{
|
|
static_cast<Application*>(events_loop.get_owner())->remove_key_handler(_channel);
|
|
static_cast<Application*>(events_loop.get_owner())->remove_mouse_handler(_channel);
|
|
DetachFunc detach_func;
|
|
_channel.get_client().for_each_channel(detach_func);
|
|
}
|
|
|
|
private:
|
|
InputsChannel& _channel;
|
|
};
|
|
|
|
class MotionMessage: public RedChannel::OutMessage, public RedPeer::OutMessage {
|
|
public:
|
|
MotionMessage(InputsChannel& channel);
|
|
virtual RedPeer::OutMessage& peer_message();
|
|
virtual void release();
|
|
|
|
private:
|
|
InputsChannel& _channel;
|
|
};
|
|
|
|
MotionMessage::MotionMessage(InputsChannel& channel)
|
|
: RedChannel::OutMessage()
|
|
, RedPeer::OutMessage(SPICE_MSGC_INPUTS_MOUSE_MOTION, sizeof(SpiceMsgcMouseMotion))
|
|
, _channel (channel)
|
|
{
|
|
}
|
|
|
|
void MotionMessage::release()
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
RedPeer::OutMessage& MotionMessage::peer_message()
|
|
{
|
|
_channel.set_motion_event(*(SpiceMsgcMouseMotion*)data());
|
|
return *this;
|
|
}
|
|
|
|
class PositionMessage: public RedChannel::OutMessage, public RedPeer::OutMessage {
|
|
public:
|
|
PositionMessage(InputsChannel& channel);
|
|
virtual RedPeer::OutMessage& peer_message();
|
|
virtual void release();
|
|
|
|
private:
|
|
InputsChannel& _channel;
|
|
};
|
|
|
|
PositionMessage::PositionMessage(InputsChannel& channel)
|
|
: RedChannel::OutMessage()
|
|
, RedPeer::OutMessage(SPICE_MSGC_INPUTS_MOUSE_POSITION, sizeof(SpiceMsgcMousePosition))
|
|
, _channel (channel)
|
|
{
|
|
}
|
|
|
|
void PositionMessage::release()
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
RedPeer::OutMessage& PositionMessage::peer_message()
|
|
{
|
|
_channel.set_position_event(*(SpiceMsgcMousePosition*)data());
|
|
return *this;
|
|
}
|
|
|
|
class InputsMessHandler: public MessageHandlerImp<InputsChannel, SPICE_CHANNEL_INPUTS> {
|
|
public:
|
|
InputsMessHandler(InputsChannel& channel)
|
|
: MessageHandlerImp<InputsChannel, SPICE_CHANNEL_INPUTS>(channel) {}
|
|
};
|
|
|
|
InputsChannel::InputsChannel(RedClient& client, uint32_t id)
|
|
: RedChannel(client, SPICE_CHANNEL_INPUTS, id, new InputsMessHandler(*this))
|
|
, _mouse_buttons_state (0)
|
|
, _mouse_dx (0)
|
|
, _mouse_dy (0)
|
|
, _mouse_x (~0)
|
|
, _mouse_y (~0)
|
|
, _display_id (-1)
|
|
, _active_motion (false)
|
|
, _motion_count (0)
|
|
, _active_modifiers_event (false)
|
|
{
|
|
InputsMessHandler* handler = static_cast<InputsMessHandler*>(get_message_handler());
|
|
handler->set_handler(SPICE_MSG_MIGRATE, &InputsChannel::handle_migrate);
|
|
handler->set_handler(SPICE_MSG_SET_ACK, &InputsChannel::handle_set_ack);
|
|
handler->set_handler(SPICE_MSG_PING, &InputsChannel::handle_ping);
|
|
handler->set_handler(SPICE_MSG_WAIT_FOR_CHANNELS, &InputsChannel::handle_wait_for_channels);
|
|
handler->set_handler(SPICE_MSG_DISCONNECTING, &InputsChannel::handle_disconnect);
|
|
handler->set_handler(SPICE_MSG_NOTIFY, &InputsChannel::handle_notify);
|
|
|
|
handler->set_handler(SPICE_MSG_INPUTS_INIT, &InputsChannel::handle_init);
|
|
handler->set_handler(SPICE_MSG_INPUTS_KEY_MODIFIERS, &InputsChannel::handle_modifaiers);
|
|
handler->set_handler(SPICE_MSG_INPUTS_MOUSE_MOTION_ACK, &InputsChannel::handle_motion_ack);
|
|
}
|
|
|
|
InputsChannel::~InputsChannel()
|
|
{
|
|
}
|
|
|
|
void InputsChannel::on_connect()
|
|
{
|
|
_motion_count = _mouse_dx = _mouse_dy = _mouse_buttons_state = _modifiers = 0;
|
|
_mouse_x = _mouse_y = ~0;
|
|
_display_id = -1;
|
|
}
|
|
|
|
void InputsChannel::on_disconnect()
|
|
{
|
|
AutoRef<RemoveInputsHandlerEvent> remove_handler_event(new RemoveInputsHandlerEvent(*this));
|
|
get_client().push_event(*remove_handler_event);
|
|
(*remove_handler_event)->wait();
|
|
}
|
|
|
|
void InputsChannel::handle_init(RedPeer::InMessage* message)
|
|
{
|
|
SpiceMsgInputsInit* init = (SpiceMsgInputsInit*)message->data();
|
|
_modifiers = init->keyboard_modifiers;
|
|
AutoRef<SetInputsHandlerEvent> set_handler_event(new SetInputsHandlerEvent(*this));
|
|
get_client().push_event(*set_handler_event);
|
|
}
|
|
|
|
void InputsChannel::handle_modifaiers(RedPeer::InMessage* message)
|
|
{
|
|
SpiceMsgInputsKeyModifiers* init = (SpiceMsgInputsKeyModifiers*)message->data();
|
|
_modifiers = init->modifiers;
|
|
Lock lock(_update_modifiers_lock);
|
|
if (_active_modifiers_event) {
|
|
return;
|
|
}
|
|
_active_modifiers_event = true;
|
|
AutoRef<KeyModifiersEvent> modifiers_event(new KeyModifiersEvent(*this));
|
|
get_client().push_event(*modifiers_event);
|
|
}
|
|
|
|
void InputsChannel::handle_motion_ack(RedPeer::InMessage* message)
|
|
{
|
|
Lock lock(_motion_lock);
|
|
if (_motion_count < SPICE_INPUT_MOTION_ACK_BUNCH) {
|
|
LOG_WARN("invalid motion count");
|
|
_motion_count = 0;
|
|
} else {
|
|
_motion_count -= SPICE_INPUT_MOTION_ACK_BUNCH;
|
|
}
|
|
if (!_active_motion && (_mouse_dx || _mouse_dy || _display_id != -1)) {
|
|
_active_motion = true;
|
|
_motion_count++;
|
|
switch (get_client().get_mouse_mode()) {
|
|
case SPICE_MOUSE_MODE_CLIENT:
|
|
post_message(new PositionMessage(*this));
|
|
break;
|
|
case SPICE_MOUSE_MODE_SERVER:
|
|
post_message(new MotionMessage(*this));
|
|
break;
|
|
default:
|
|
THROW("invalid mouse mode");
|
|
}
|
|
}
|
|
}
|
|
|
|
void InputsChannel::set_motion_event(SpiceMsgcMouseMotion& motion)
|
|
{
|
|
Lock lock(_motion_lock);
|
|
motion.buttons_state = _mouse_buttons_state;
|
|
motion.dx = _mouse_dx;
|
|
motion.dy = _mouse_dy;
|
|
_mouse_dx = _mouse_dy = 0;
|
|
_active_motion = false;
|
|
}
|
|
|
|
void InputsChannel::set_position_event(SpiceMsgcMousePosition& position)
|
|
{
|
|
Lock lock(_motion_lock);
|
|
position.buttons_state = _mouse_buttons_state;
|
|
position.x = _mouse_x;
|
|
position.y = _mouse_y;
|
|
position.display_id = _display_id;
|
|
_mouse_x = _mouse_y = ~0;
|
|
_display_id = -1;
|
|
_active_motion = false;
|
|
}
|
|
|
|
void InputsChannel::on_mouse_motion(int dx, int dy, int buttons_state)
|
|
{
|
|
Lock lock(_motion_lock);
|
|
_mouse_buttons_state = buttons_state;
|
|
_mouse_dx += dx;
|
|
_mouse_dy += dy;
|
|
if (!_active_motion && _motion_count < SPICE_INPUT_MOTION_ACK_BUNCH * 2) {
|
|
_active_motion = true;
|
|
_motion_count++;
|
|
post_message(new MotionMessage(*this));
|
|
}
|
|
}
|
|
|
|
void InputsChannel::on_mouse_position(int x, int y, int buttons_state, int display_id)
|
|
{
|
|
Lock lock(_motion_lock);
|
|
_mouse_buttons_state = buttons_state;
|
|
_mouse_x = x;
|
|
_mouse_y = y;
|
|
_display_id = display_id;
|
|
if (!_active_motion && _motion_count < SPICE_INPUT_MOTION_ACK_BUNCH * 2) {
|
|
_active_motion = true;
|
|
_motion_count++;
|
|
post_message(new PositionMessage(*this));
|
|
}
|
|
}
|
|
|
|
void InputsChannel::on_migrate()
|
|
{
|
|
_motion_count = _active_motion ? 1 : 0;
|
|
}
|
|
|
|
void InputsChannel::on_mouse_down(int button, int buttons_state)
|
|
{
|
|
Message* message;
|
|
|
|
message = new Message(SPICE_MSGC_INPUTS_MOUSE_PRESS, sizeof(SpiceMsgcMouseRelease));
|
|
SpiceMsgcMousePress* event = (SpiceMsgcMousePress*)message->data();
|
|
event->button = button;
|
|
event->buttons_state = buttons_state;
|
|
post_message(message);
|
|
}
|
|
|
|
void InputsChannel::on_mouse_up(int button, int buttons_state)
|
|
{
|
|
Message* message;
|
|
|
|
message = new Message(SPICE_MSGC_INPUTS_MOUSE_RELEASE, sizeof(SpiceMsgcMouseRelease));
|
|
SpiceMsgcMouseRelease* event = (SpiceMsgcMouseRelease*)message->data();
|
|
event->button = button;
|
|
event->buttons_state = buttons_state;
|
|
post_message(message);
|
|
}
|
|
|
|
InputsChannel::KeyInfo InputsChannel::_scan_table[REDKEY_NUM_KEYS];
|
|
|
|
uint32_t InputsChannel::get_make_scan_code(RedKey key)
|
|
{
|
|
return _scan_table[key].make_scan;
|
|
}
|
|
|
|
uint32_t InputsChannel::get_break_scan_code(RedKey key)
|
|
{
|
|
return _scan_table[key].break_scan;
|
|
}
|
|
|
|
void InputsChannel::on_key_down(RedKey key)
|
|
{
|
|
uint32_t scan_code = get_make_scan_code(key);
|
|
if (!scan_code) {
|
|
LOG_WARN("no make code for %d", key);
|
|
return;
|
|
}
|
|
|
|
Message* message = new Message(SPICE_MSGC_INPUTS_KEY_DOWN, sizeof(SpiceMsgcKeyDown));
|
|
SpiceMsgcKeyDown* event = (SpiceMsgcKeyDown*)message->data();
|
|
event->code = scan_code;
|
|
post_message(message);
|
|
}
|
|
|
|
void InputsChannel::on_key_up(RedKey key)
|
|
{
|
|
uint32_t scan_code = get_break_scan_code(key);
|
|
if (!scan_code) {
|
|
LOG_WARN("no break code for %d", key);
|
|
return;
|
|
}
|
|
|
|
Message* message = new Message(SPICE_MSGC_INPUTS_KEY_UP, sizeof(SpiceMsgcKeyUp));
|
|
SpiceMsgcKeyUp* event = (SpiceMsgcKeyUp*)message->data();
|
|
event->code = scan_code;
|
|
post_message(message);
|
|
}
|
|
|
|
void InputsChannel::set_local_modifiers()
|
|
{
|
|
unsigned int modifiers = 0;
|
|
|
|
if (_modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK) {
|
|
modifiers |= Platform::SCROLL_LOCK_MODIFIER;
|
|
}
|
|
|
|
if (_modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK) {
|
|
modifiers |= Platform::NUM_LOCK_MODIFIER;
|
|
}
|
|
|
|
if (_modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK) {
|
|
modifiers |= Platform::CAPS_LOCK_MODIFIER;
|
|
}
|
|
|
|
Platform::set_keyboard_lock_modifiers(_modifiers);
|
|
}
|
|
|
|
void InputsChannel::on_focus_in()
|
|
{
|
|
#ifdef SYNC_REMOTH_MODIFIRES
|
|
Message* message = new Message(SPICE_MSGC_INPUTS_KEY_MODIFIERS, sizeof(SpiceMsgcKeyDown));
|
|
SpiceMsgcKeyModifiers* modifiers = (SpiceMsgcKeyModifiers*)message->data();
|
|
modifiers->modifiers = Platform::get_keyboard_lock_modifiers();
|
|
post_message(message);
|
|
#else
|
|
set_local_modifiers();
|
|
#endif
|
|
}
|
|
|
|
void InputsChannel::init_scan_code(int index)
|
|
{
|
|
ASSERT((index & 0x80) == 0);
|
|
_scan_table[index].make_scan = index;
|
|
_scan_table[index].break_scan = index | 0x80;
|
|
}
|
|
|
|
void InputsChannel::init_korean_scan_code(int index)
|
|
{
|
|
_scan_table[index].make_scan = index;
|
|
_scan_table[index].break_scan = index;
|
|
}
|
|
|
|
void InputsChannel::init_escape_scan_code(int index)
|
|
{
|
|
ASSERT(((index - REDKEY_ESCAPE_BASE) & 0x80) == 0);
|
|
_scan_table[index].make_scan = 0xe0 | ((index - REDKEY_ESCAPE_BASE) << 8);
|
|
_scan_table[index].break_scan = _scan_table[index].make_scan | 0x8000;
|
|
}
|
|
|
|
void InputsChannel::init_pause_scan_code()
|
|
{
|
|
_scan_table[REDKEY_PAUSE].make_scan = 0x451de1;
|
|
_scan_table[REDKEY_PAUSE].break_scan = 0xc59de1;
|
|
}
|
|
|
|
void InputsChannel::init_scan_table()
|
|
{
|
|
memset(_scan_table, 0, sizeof(_scan_table));
|
|
init_scan_code(REDKEY_ESCAPE);
|
|
init_scan_code(REDKEY_1);
|
|
init_scan_code(REDKEY_2);
|
|
init_scan_code(REDKEY_3);
|
|
init_scan_code(REDKEY_4);
|
|
init_scan_code(REDKEY_5);
|
|
init_scan_code(REDKEY_6);
|
|
init_scan_code(REDKEY_7);
|
|
init_scan_code(REDKEY_8);
|
|
init_scan_code(REDKEY_9);
|
|
init_scan_code(REDKEY_0);
|
|
init_scan_code(REDKEY_MINUS);
|
|
init_scan_code(REDKEY_EQUALS);
|
|
init_scan_code(REDKEY_BACKSPACE);
|
|
init_scan_code(REDKEY_TAB);
|
|
init_scan_code(REDKEY_Q);
|
|
init_scan_code(REDKEY_W);
|
|
init_scan_code(REDKEY_E);
|
|
init_scan_code(REDKEY_R);
|
|
init_scan_code(REDKEY_T);
|
|
init_scan_code(REDKEY_Y);
|
|
init_scan_code(REDKEY_U);
|
|
init_scan_code(REDKEY_I);
|
|
init_scan_code(REDKEY_O);
|
|
init_scan_code(REDKEY_P);
|
|
init_scan_code(REDKEY_L_BRACKET);
|
|
init_scan_code(REDKEY_R_BRACKET);
|
|
init_scan_code(REDKEY_ENTER);
|
|
init_scan_code(REDKEY_L_CTRL);
|
|
init_scan_code(REDKEY_A);
|
|
init_scan_code(REDKEY_S);
|
|
init_scan_code(REDKEY_D);
|
|
init_scan_code(REDKEY_F);
|
|
init_scan_code(REDKEY_G);
|
|
init_scan_code(REDKEY_H);
|
|
init_scan_code(REDKEY_J);
|
|
init_scan_code(REDKEY_K);
|
|
init_scan_code(REDKEY_L);
|
|
init_scan_code(REDKEY_SEMICOLON);
|
|
init_scan_code(REDKEY_QUOTE);
|
|
init_scan_code(REDKEY_BACK_QUOTE);
|
|
init_scan_code(REDKEY_L_SHIFT);
|
|
init_scan_code(REDKEY_BACK_SLASH);
|
|
init_scan_code(REDKEY_Z);
|
|
init_scan_code(REDKEY_X);
|
|
init_scan_code(REDKEY_C);
|
|
init_scan_code(REDKEY_V);
|
|
init_scan_code(REDKEY_B);
|
|
init_scan_code(REDKEY_N);
|
|
init_scan_code(REDKEY_M);
|
|
init_scan_code(REDKEY_COMMA);
|
|
init_scan_code(REDKEY_PERIOD);
|
|
init_scan_code(REDKEY_SLASH);
|
|
init_scan_code(REDKEY_R_SHIFT);
|
|
init_scan_code(REDKEY_PAD_MULTIPLY);
|
|
init_scan_code(REDKEY_L_ALT);
|
|
init_scan_code(REDKEY_SPACE);
|
|
init_scan_code(REDKEY_CAPS_LOCK);
|
|
init_scan_code(REDKEY_F1);
|
|
init_scan_code(REDKEY_F2);
|
|
init_scan_code(REDKEY_F3);
|
|
init_scan_code(REDKEY_F4);
|
|
init_scan_code(REDKEY_F5);
|
|
init_scan_code(REDKEY_F6);
|
|
init_scan_code(REDKEY_F7);
|
|
init_scan_code(REDKEY_F8);
|
|
init_scan_code(REDKEY_F9);
|
|
init_scan_code(REDKEY_F10);
|
|
init_scan_code(REDKEY_NUM_LOCK);
|
|
init_scan_code(REDKEY_SCROLL_LOCK);
|
|
init_scan_code(REDKEY_PAD_7);
|
|
init_scan_code(REDKEY_PAD_8);
|
|
init_scan_code(REDKEY_PAD_9);
|
|
init_scan_code(REDKEY_PAD_MINUS);
|
|
init_scan_code(REDKEY_PAD_4);
|
|
init_scan_code(REDKEY_PAD_5);
|
|
init_scan_code(REDKEY_PAD_6);
|
|
init_scan_code(REDKEY_PAD_PLUS);
|
|
init_scan_code(REDKEY_PAD_1);
|
|
init_scan_code(REDKEY_PAD_2);
|
|
init_scan_code(REDKEY_PAD_3);
|
|
init_scan_code(REDKEY_PAD_0);
|
|
init_scan_code(REDKEY_PAD_POINT);
|
|
|
|
init_scan_code(REDKEY_EUROPEAN);
|
|
init_scan_code(REDKEY_F11);
|
|
init_scan_code(REDKEY_F12);
|
|
|
|
init_scan_code(REDKEY_JAPANESE_HIRAGANA_KATAKANA);
|
|
init_scan_code(REDKEY_JAPANESE_BACKSLASH);
|
|
init_scan_code(REDKEY_JAPANESE_HENKAN);
|
|
init_scan_code(REDKEY_JAPANESE_MUHENKAN);
|
|
init_scan_code(REDKEY_JAPANESE_YEN);
|
|
|
|
init_korean_scan_code(REDKEY_KOREAN_HANGUL);
|
|
init_korean_scan_code(REDKEY_KOREAN_HANGUL_HANJA);
|
|
|
|
init_escape_scan_code(REDKEY_ESCAPE_BASE);
|
|
init_escape_scan_code(REDKEY_PAD_ENTER);
|
|
init_escape_scan_code(REDKEY_R_CTRL);
|
|
init_escape_scan_code(REDKEY_FAKE_L_SHIFT);
|
|
init_escape_scan_code(REDKEY_PAD_DIVIDE);
|
|
init_escape_scan_code(REDKEY_FAKE_R_SHIFT);
|
|
init_escape_scan_code(REDKEY_CTRL_PRINT_SCREEN);
|
|
init_escape_scan_code(REDKEY_R_ALT);
|
|
init_escape_scan_code(REDKEY_CTRL_BREAK);
|
|
init_escape_scan_code(REDKEY_HOME);
|
|
init_escape_scan_code(REDKEY_UP);
|
|
init_escape_scan_code(REDKEY_PAGEUP);
|
|
init_escape_scan_code(REDKEY_LEFT);
|
|
init_escape_scan_code(REDKEY_RIGHT);
|
|
init_escape_scan_code(REDKEY_END);
|
|
init_escape_scan_code(REDKEY_DOWN);
|
|
init_escape_scan_code(REDKEY_PAGEDOWN);
|
|
init_escape_scan_code(REDKEY_INSERT);
|
|
init_escape_scan_code(REDKEY_DELETE);
|
|
init_escape_scan_code(REDKEY_LEFT_CMD);
|
|
init_escape_scan_code(REDKEY_RIGHT_CMD);
|
|
init_escape_scan_code(REDKEY_MENU);
|
|
|
|
init_pause_scan_code();
|
|
}
|
|
|
|
class InitGlobals {
|
|
public:
|
|
InitGlobals()
|
|
{
|
|
InputsChannel::init_scan_table();
|
|
}
|
|
};
|
|
|
|
static InitGlobals init_globals;
|
|
|
|
class InputsFactory: public ChannelFactory {
|
|
public:
|
|
InputsFactory() : ChannelFactory(SPICE_CHANNEL_INPUTS) {}
|
|
virtual RedChannel* construct(RedClient& client, uint32_t id)
|
|
{
|
|
return new InputsChannel(client, id);
|
|
}
|
|
};
|
|
|
|
static InputsFactory factory;
|
|
|
|
ChannelFactory& InputsChannel::Factory()
|
|
{
|
|
return factory;
|
|
}
|
|
|