/* 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 . */ #ifdef HAVE_CONFIG_H #include #endif #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_REMOTE_MODIFIERS 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(channel).attach_inputs(&_channel); } return true; } public: InputsChannel& _channel; }; virtual void response(AbstractProcessLoop& events_loop) { static_cast(events_loop.get_owner())->set_key_handler(_channel); static_cast(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(channel).detach_inputs(); } return true; } }; virtual void do_response(AbstractProcessLoop& events_loop) { static_cast(events_loop.get_owner())->remove_key_handler(_channel); static_cast(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) , _channel (channel) { } void MotionMessage::release() { delete this; } RedPeer::OutMessage& MotionMessage::peer_message() { _channel.marshall_motion_event(_marshaller); 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) , _channel (channel) { } void PositionMessage::release() { delete this; } RedPeer::OutMessage& PositionMessage::peer_message() { _channel.marshall_position_event(_marshaller); return *this; } class InputsMessHandler: public MessageHandlerImp { public: InputsMessHandler(InputsChannel& channel) : MessageHandlerImp(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(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_modifiers); 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 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 set_handler_event(new SetInputsHandlerEvent(*this)); get_client().push_event(*set_handler_event); } void InputsChannel::handle_modifiers(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 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::marshall_motion_event(SpiceMarshaller *marshaller) { 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; _marshallers->msgc_inputs_mouse_motion(marshaller, &motion); } void InputsChannel::marshall_position_event(SpiceMarshaller *marshaller) { 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; _marshallers->msgc_inputs_mouse_position(marshaller, &position); } 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); SpiceMsgcMousePress event; event.button = button; event.buttons_state = buttons_state; _marshallers->msgc_inputs_mouse_press(message->marshaller(), &event); post_message(message); } void InputsChannel::on_mouse_up(int button, int buttons_state) { Message* message; message = new Message(SPICE_MSGC_INPUTS_MOUSE_RELEASE); SpiceMsgcMouseRelease event; event.button = button; event.buttons_state = buttons_state; _marshallers->msgc_inputs_mouse_release(message->marshaller(), &event); 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); SpiceMsgcKeyDown event; event.code = scan_code; _marshallers->msgc_inputs_key_down(message->marshaller(), &event); 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); SpiceMsgcKeyUp event; event.code = scan_code; _marshallers->msgc_inputs_key_up(message->marshaller(), &event); 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() { Lock lock(_update_modifiers_lock); _active_modifiers_event = false; _on_focus_modifiers = Platform::get_keyboard_lock_modifiers(); #ifdef SYNC_REMOTE_MODIFIERS Message* message = new Message(SPICE_MSGC_INPUTS_KEY_MODIFIERS); SpiceMsgcKeyModifiers modifiers; modifiers.modifiers = _on_focus_modifiers; _marshallers->msgc_inputs_key_modifiers(message->marshaller(), &modifiers); post_message(message); #else set_local_modifiers(); #endif } void InputsChannel::on_focus_out() { Lock lock(_update_modifiers_lock); _active_modifiers_event = true; #ifndef SYNC_REMOTE_MODIFIERS _modifiers = _on_focus_modifiers; 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_MUTE); init_escape_scan_code(REDKEY_FAKE_L_SHIFT); init_escape_scan_code(REDKEY_VOLUME_DOWN); init_escape_scan_code(REDKEY_VOLUME_UP); 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; }