/* Copyright (C) 2009 Red Hat, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "common.h" #include "inputs_channel.h" #include "utils.h" #include "debug.h" #include "red_client.h" #include "application.h" #define SYNC_REMOTH_MODIFIRES class SetInputsHandlerEvent: public Event { public: SetInputsHandlerEvent(InputsChannel& channel) : _channel (channel) {} virtual void response(AbstractProcessLoop& events_loop) { static_cast(events_loop.get_owner())->set_inputs_handler(_channel); } 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) {} virtual void do_response(AbstractProcessLoop& events_loop) { static_cast(events_loop.get_owner())->remove_inputs_handler(_channel); } 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(REDC_INPUTS_MOUSE_MOTION, sizeof(RedcMouseMotion)) , _channel (channel) { } void MotionMessage::release() { delete this; } RedPeer::OutMessage& MotionMessage::peer_message() { _channel.set_motion_event(*(RedcMouseMotion*)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(REDC_INPUTS_MOUSE_POSITION, sizeof(RedcMousePosition)) , _channel (channel) { } void PositionMessage::release() { delete this; } RedPeer::OutMessage& PositionMessage::peer_message() { _channel.set_position_event(*(RedcMousePosition*)data()); return *this; } class InputsMessHandler: public MessageHandlerImp { public: InputsMessHandler(InputsChannel& channel) : MessageHandlerImp(channel) {} }; InputsChannel::InputsChannel(RedClient& client, uint32_t id) : RedChannel(client, RED_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(RED_MIGRATE, &InputsChannel::handle_migrate, 0); handler->set_handler(RED_SET_ACK, &InputsChannel::handle_set_ack, sizeof(RedSetAck)); handler->set_handler(RED_PING, &InputsChannel::handle_ping, sizeof(RedPing)); handler->set_handler(RED_WAIT_FOR_CHANNELS, &InputsChannel::handle_wait_for_channels, sizeof(RedWaitForChannels)); handler->set_handler(RED_DISCONNECTING, &InputsChannel::handle_disconnect, sizeof(RedDisconnect)); handler->set_handler(RED_NOTIFY, &InputsChannel::handle_notify, sizeof(RedNotify)); handler->set_handler(RED_INPUTS_INIT, &InputsChannel::handle_init, sizeof(RedInputsInit)); handler->set_handler(RED_INPUTS_KEY_MODIFAIERS, &InputsChannel::handle_modifaiers, sizeof(RedKeyModifiers)); handler->set_handler(RED_INPUTS_MOUSE_MOTION_ACK, &InputsChannel::handle_motion_ack, 0); } 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) { RedInputsInit* init = (RedInputsInit*)message->data(); _modifiers = init->keyboard_modifiers; AutoRef set_handler_event(new SetInputsHandlerEvent(*this)); get_client().push_event(*set_handler_event); } void InputsChannel::handle_modifaiers(RedPeer::InMessage* message) { RedKeyModifiers* init = (RedKeyModifiers*)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 < RED_MOTION_ACK_BUNCH) { LOG_WARN("invalid motion count"); _motion_count = 0; } else { _motion_count -= RED_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 RED_MOUSE_MODE_CLIENT: post_message(new PositionMessage(*this)); break; case RED_MOUSE_MODE_SERVER: post_message(new MotionMessage(*this)); break; default: THROW("invalid mouse mode"); } } } void InputsChannel::set_motion_event(RedcMouseMotion& 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(RedcMousePosition& 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 < RED_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 < RED_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(REDC_INPUTS_MOUSE_PRESS, sizeof(RedcMouseRelease)); RedcMousePress* event = (RedcMousePress*)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(REDC_INPUTS_MOUSE_RELEASE, sizeof(RedcMouseRelease)); RedcMouseRelease* event = (RedcMouseRelease*)message->data(); event->button = button; event->buttons_state = buttons_state; post_message(message); } void InputsChannel::on_key_down(uint32_t scan_code) { Message* message = new Message(REDC_INPUTS_KEY_DOWN, sizeof(RedcKeyDown)); RedcKeyDown* event = (RedcKeyDown*)message->data(); event->code = scan_code; post_message(message); } void InputsChannel::on_key_up(uint32_t scan_code) { Message* message = new Message(REDC_INPUTS_KEY_UP, sizeof(RedcKeyUp)); RedcKeyUp* event = (RedcKeyUp*)message->data(); event->code = scan_code; post_message(message); } void InputsChannel::set_local_modifiers() { unsigned int modifiers = 0; if (_modifiers & RED_SCROLL_LOCK_MODIFIER) { modifiers |= Platform::SCROLL_LOCK_MODIFIER; } if (_modifiers & RED_NUM_LOCK_MODIFIER) { modifiers |= Platform::NUM_LOCK_MODIFIER; } if (_modifiers & RED_CAPS_LOCK_MODIFIER) { modifiers |= Platform::CAPS_LOCK_MODIFIER; } Platform::set_keyboard_lock_modifiers(_modifiers); } void InputsChannel::on_focus_in() { #ifdef SYNC_REMOTH_MODIFIRES Message* message = new Message(REDC_INPUTS_KEY_MODIFAIERS, sizeof(RedcKeyDown)); RedcKeyModifiers* modifiers = (RedcKeyModifiers*)message->data(); modifiers->modifiers = Platform::get_keyboard_lock_modifiers(); post_message(message); #else set_local_modifiers(); #endif } class InputsFactory: public ChannelFactory { public: InputsFactory() : ChannelFactory(RED_CHANNEL_INPUTS) {} virtual RedChannel* construct(RedClient& client, uint32_t id) { return new InputsChannel(client, id); } }; static InputsFactory factory; ChannelFactory& InputsChannel::Factory() { return factory; }