blob: 6ff6164add558a5ff006bc22d5a38b0f5ea16f77 [file] [log] [blame]
[email protected]91e4b7f62012-01-25 23:23:021// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]44f60762011-03-23 12:13:352// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "remoting/host/client_session.h"
6
[email protected]4fe827a2011-08-10 03:30:197#include <algorithm>
8
[email protected]ee910fd2011-11-10 18:23:319#include "base/message_loop_proxy.h"
[email protected]995c2c6d2011-09-15 05:08:2510#include "remoting/host/capturer.h"
[email protected]c78669c92011-06-13 22:42:3811#include "remoting/proto/event.pb.h"
12
13// The number of remote mouse events to record for the purpose of eliminating
14// "echoes" detected by the local input detector. The value should be large
15// enough to cope with the fact that multiple events might be injected before
16// any echoes are detected.
[email protected]9a7aee22011-06-24 22:31:2317static const unsigned int kNumRemoteMousePositions = 50;
[email protected]c78669c92011-06-13 22:42:3818
19// The number of milliseconds for which to block remote input when local input
20// is received.
21static const int64 kRemoteBlockTimeoutMillis = 2000;
[email protected]44f60762011-03-23 12:13:3522
23namespace remoting {
24
[email protected]42f5c7e2011-08-30 16:33:4025using protocol::KeyEvent;
[email protected]b25ff3b2011-09-13 18:17:3026using protocol::MouseEvent;
[email protected]42f5c7e2011-08-30 16:33:4027
[email protected]44f60762011-03-23 12:13:3528ClientSession::ClientSession(
29 EventHandler* event_handler,
[email protected]ec6411872011-11-11 03:28:5530 protocol::ConnectionToClient* connection,
[email protected]995c2c6d2011-09-15 05:08:2531 protocol::InputStub* input_stub,
32 Capturer* capturer)
[email protected]44f60762011-03-23 12:13:3533 : event_handler_(event_handler),
[email protected]4ea2c7c2011-03-31 14:20:0634 connection_(connection),
[email protected]f19d9bd2011-09-13 05:21:1135 client_jid_(connection->session()->jid()),
[email protected]4ea2c7c2011-03-31 14:20:0636 input_stub_(input_stub),
[email protected]995c2c6d2011-09-15 05:08:2537 capturer_(capturer),
[email protected]c78669c92011-06-13 22:42:3838 authenticated_(false),
[email protected]35c14ee2011-06-20 19:32:4539 awaiting_continue_approval_(false),
[email protected]c78669c92011-06-13 22:42:3840 remote_mouse_button_state_(0) {
[email protected]ee910fd2011-11-10 18:23:3141 connection_->SetEventHandler(this);
42
43 // TODO(sergeyu): Currently ConnectionToClient expects stubs to be
44 // set before channels are connected. Make it possible to set stubs
45 // later and set them only when connection is authenticated.
46 connection_->set_host_stub(this);
47 connection_->set_input_stub(this);
[email protected]44f60762011-03-23 12:13:3548}
49
50ClientSession::~ClientSession() {
51}
52
[email protected]b25ff3b2011-09-13 18:17:3053void ClientSession::InjectKeyEvent(const KeyEvent& event) {
[email protected]ec6411872011-11-11 03:28:5554 DCHECK(CalledOnValidThread());
55
[email protected]35c14ee2011-06-20 19:32:4556 if (authenticated_ && !ShouldIgnoreRemoteKeyboardInput(event)) {
[email protected]86c5a1e2011-06-29 20:50:1557 RecordKeyEvent(event);
[email protected]b25ff3b2011-09-13 18:17:3058 input_stub_->InjectKeyEvent(event);
[email protected]4ea2c7c2011-03-31 14:20:0659 }
60}
61
[email protected]b25ff3b2011-09-13 18:17:3062void ClientSession::InjectMouseEvent(const MouseEvent& event) {
[email protected]ec6411872011-11-11 03:28:5563 DCHECK(CalledOnValidThread());
64
[email protected]35c14ee2011-06-20 19:32:4565 if (authenticated_ && !ShouldIgnoreRemoteMouseInput(event)) {
[email protected]b67fb9302011-09-26 01:55:5266 RecordMouseButtonState(event);
[email protected]995c2c6d2011-09-15 05:08:2567 MouseEvent event_to_inject = event;
[email protected]b25ff3b2011-09-13 18:17:3068 if (event.has_x() && event.has_y()) {
[email protected]995c2c6d2011-09-15 05:08:2569 // In case the client sends events with off-screen coordinates, modify
70 // the event to lie within the current screen area. This is better than
71 // simply discarding the event, which might lose a button-up event at the
72 // end of a drag'n'drop (or cause other related problems).
[email protected]bcad2682011-09-30 20:35:2673 SkIPoint pos(SkIPoint::Make(event.x(), event.y()));
74 const SkISize& screen = capturer_->size_most_recent();
75 pos.setX(std::max(0, std::min(screen.width() - 1, pos.x())));
76 pos.setY(std::max(0, std::min(screen.height() - 1, pos.y())));
[email protected]995c2c6d2011-09-15 05:08:2577 event_to_inject.set_x(pos.x());
78 event_to_inject.set_y(pos.y());
79
[email protected]b67fb9302011-09-26 01:55:5280 // Record the mouse position so we can use it if we need to inject
81 // fake mouse button events. Note that we need to do this after we
82 // clamp the values to the screen area.
83 remote_mouse_pos_ = pos;
84
[email protected]4fe827a2011-08-10 03:30:1985 injected_mouse_positions_.push_back(pos);
86 if (injected_mouse_positions_.size() > kNumRemoteMousePositions) {
87 VLOG(1) << "Injected mouse positions queue full.";
88 injected_mouse_positions_.pop_front();
[email protected]c78669c92011-06-13 22:42:3889 }
90 }
[email protected]995c2c6d2011-09-15 05:08:2591 input_stub_->InjectMouseEvent(event_to_inject);
[email protected]4ea2c7c2011-03-31 14:20:0692 }
[email protected]44f60762011-03-23 12:13:3593}
94
[email protected]ee910fd2011-11-10 18:23:3195void ClientSession::OnConnectionOpened(
96 protocol::ConnectionToClient* connection) {
[email protected]ec6411872011-11-11 03:28:5597 DCHECK(CalledOnValidThread());
[email protected]ee910fd2011-11-10 18:23:3198 DCHECK_EQ(connection_.get(), connection);
99 authenticated_ = true;
100 event_handler_->OnSessionAuthenticated(this);
101}
102
103void ClientSession::OnConnectionClosed(
104 protocol::ConnectionToClient* connection) {
[email protected]ec6411872011-11-11 03:28:55105 DCHECK(CalledOnValidThread());
[email protected]ee910fd2011-11-10 18:23:31106 DCHECK_EQ(connection_.get(), connection);
[email protected]ee910fd2011-11-10 18:23:31107 event_handler_->OnSessionClosed(this);
[email protected]ee910fd2011-11-10 18:23:31108}
109
110void ClientSession::OnConnectionFailed(
[email protected]1f249e22011-11-29 20:19:59111 protocol::ConnectionToClient* connection,
[email protected]204a9e32012-03-02 05:42:58112 protocol::ErrorCode error) {
[email protected]ec6411872011-11-11 03:28:55113 DCHECK(CalledOnValidThread());
[email protected]ee910fd2011-11-10 18:23:31114 DCHECK_EQ(connection_.get(), connection);
[email protected]204a9e32012-03-02 05:42:58115 if (error == protocol::AUTHENTICATION_FAILED)
[email protected]1f249e22011-11-29 20:19:59116 event_handler_->OnSessionAuthenticationFailed(this);
[email protected]ee910fd2011-11-10 18:23:31117 // TODO(sergeyu): Log failure reason?
[email protected]ee910fd2011-11-10 18:23:31118 event_handler_->OnSessionClosed(this);
[email protected]ee910fd2011-11-10 18:23:31119}
120
121void ClientSession::OnSequenceNumberUpdated(
122 protocol::ConnectionToClient* connection, int64 sequence_number) {
[email protected]ec6411872011-11-11 03:28:55123 DCHECK(CalledOnValidThread());
[email protected]ee910fd2011-11-10 18:23:31124 DCHECK_EQ(connection_.get(), connection);
125 event_handler_->OnSessionSequenceNumber(this, sequence_number);
126}
127
[email protected]17af2ab2012-02-02 04:07:52128void ClientSession::OnRouteChange(
129 protocol::ConnectionToClient* connection,
130 const std::string& channel_name,
131 const net::IPEndPoint& remote_end_point,
132 const net::IPEndPoint& local_end_point) {
[email protected]91e4b7f62012-01-25 23:23:02133 DCHECK(CalledOnValidThread());
134 DCHECK_EQ(connection_.get(), connection);
[email protected]17af2ab2012-02-02 04:07:52135 event_handler_->OnSessionRouteChange(this, channel_name, remote_end_point,
136 local_end_point);
[email protected]91e4b7f62012-01-25 23:23:02137}
138
[email protected]ee910fd2011-11-10 18:23:31139void ClientSession::Disconnect() {
[email protected]ec6411872011-11-11 03:28:55140 DCHECK(CalledOnValidThread());
141 DCHECK(connection_.get());
[email protected]4ea2c7c2011-03-31 14:20:06142 authenticated_ = false;
[email protected]ee910fd2011-11-10 18:23:31143 RestoreEventState();
[email protected]a46bcef2011-11-11 01:27:23144
145 // This triggers OnSessionClosed() and the session may be destroyed
146 // as the result, so this call must be the last in this method.
147 connection_->Disconnect();
[email protected]44f60762011-03-23 12:13:35148}
149
[email protected]bcad2682011-09-30 20:35:26150void ClientSession::LocalMouseMoved(const SkIPoint& mouse_pos) {
[email protected]ec6411872011-11-11 03:28:55151 DCHECK(CalledOnValidThread());
152
[email protected]c78669c92011-06-13 22:42:38153 // If this is a genuine local input event (rather than an echo of a remote
154 // input event that we've just injected), then ignore remote inputs for a
155 // short time.
[email protected]bcad2682011-09-30 20:35:26156 std::list<SkIPoint>::iterator found_position =
[email protected]4fe827a2011-08-10 03:30:19157 std::find(injected_mouse_positions_.begin(),
158 injected_mouse_positions_.end(), mouse_pos);
159 if (found_position != injected_mouse_positions_.end()) {
160 // Remove it from the list, and any positions that were added before it,
161 // if any. This is because the local input monitor is assumed to receive
162 // injected mouse position events in the order in which they were injected
163 // (if at all). If the position is found somewhere other than the front of
164 // the queue, this would be because the earlier positions weren't
165 // successfully injected (or the local input monitor might have skipped over
166 // some positions), and not because the events were out-of-sequence. These
167 // spurious positions should therefore be discarded.
168 injected_mouse_positions_.erase(injected_mouse_positions_.begin(),
169 ++found_position);
[email protected]c78669c92011-06-13 22:42:38170 } else {
171 latest_local_input_time_ = base::Time::Now();
172 }
173}
174
[email protected]35c14ee2011-06-20 19:32:45175bool ClientSession::ShouldIgnoreRemoteMouseInput(
[email protected]b25ff3b2011-09-13 18:17:30176 const protocol::MouseEvent& event) const {
[email protected]ec6411872011-11-11 03:28:55177 DCHECK(CalledOnValidThread());
178
[email protected]c78669c92011-06-13 22:42:38179 // If the last remote input event was a click or a drag, then it's not safe
[email protected]35c14ee2011-06-20 19:32:45180 // to block remote mouse events. For example, it might result in the host
181 // missing the mouse-up event and being stuck with the button pressed.
[email protected]c78669c92011-06-13 22:42:38182 if (remote_mouse_button_state_ != 0)
183 return false;
[email protected]35c14ee2011-06-20 19:32:45184 // Otherwise, if the host user has not yet approved the continuation of the
185 // connection, then ignore remote mouse events.
186 if (awaiting_continue_approval_)
187 return true;
188 // Otherwise, ignore remote mouse events if the local mouse moved recently.
[email protected]c78669c92011-06-13 22:42:38189 int64 millis = (base::Time::Now() - latest_local_input_time_)
190 .InMilliseconds();
191 if (millis < kRemoteBlockTimeoutMillis)
192 return true;
193 return false;
194}
195
[email protected]35c14ee2011-06-20 19:32:45196bool ClientSession::ShouldIgnoreRemoteKeyboardInput(
[email protected]b25ff3b2011-09-13 18:17:30197 const KeyEvent& event) const {
[email protected]ec6411872011-11-11 03:28:55198 DCHECK(CalledOnValidThread());
199
[email protected]35c14ee2011-06-20 19:32:45200 // If the host user has not yet approved the continuation of the connection,
201 // then all remote keyboard input is ignored, except to release keys that
202 // were already pressed.
203 if (awaiting_continue_approval_) {
[email protected]b25ff3b2011-09-13 18:17:30204 return event.pressed() ||
205 (pressed_keys_.find(event.keycode()) == pressed_keys_.end());
[email protected]35c14ee2011-06-20 19:32:45206 }
207 return false;
208}
209
[email protected]b25ff3b2011-09-13 18:17:30210void ClientSession::RecordKeyEvent(const KeyEvent& event) {
[email protected]ec6411872011-11-11 03:28:55211 DCHECK(CalledOnValidThread());
212
[email protected]b25ff3b2011-09-13 18:17:30213 if (event.pressed()) {
214 pressed_keys_.insert(event.keycode());
[email protected]86c5a1e2011-06-29 20:50:15215 } else {
[email protected]b25ff3b2011-09-13 18:17:30216 pressed_keys_.erase(event.keycode());
[email protected]86c5a1e2011-06-29 20:50:15217 }
218}
219
[email protected]b67fb9302011-09-26 01:55:52220void ClientSession::RecordMouseButtonState(const MouseEvent& event) {
[email protected]ec6411872011-11-11 03:28:55221 DCHECK(CalledOnValidThread());
222
[email protected]b67fb9302011-09-26 01:55:52223 if (event.has_button() && event.has_button_down()) {
224 // Button values are defined in remoting/proto/event.proto.
225 if (event.button() >= 1 && event.button() < MouseEvent::BUTTON_MAX) {
226 uint32 button_change = 1 << (event.button() - 1);
227 if (event.button_down()) {
228 remote_mouse_button_state_ |= button_change;
229 } else {
230 remote_mouse_button_state_ &= ~button_change;
231 }
232 }
233 }
234}
235
236void ClientSession::RestoreEventState() {
[email protected]ec6411872011-11-11 03:28:55237 DCHECK(CalledOnValidThread());
238
[email protected]b67fb9302011-09-26 01:55:52239 // Undo any currently pressed keys.
[email protected]86c5a1e2011-06-29 20:50:15240 std::set<int>::iterator i;
241 for (i = pressed_keys_.begin(); i != pressed_keys_.end(); ++i) {
[email protected]b25ff3b2011-09-13 18:17:30242 KeyEvent key;
243 key.set_keycode(*i);
244 key.set_pressed(false);
245 input_stub_->InjectKeyEvent(key);
[email protected]86c5a1e2011-06-29 20:50:15246 }
247 pressed_keys_.clear();
[email protected]b67fb9302011-09-26 01:55:52248
249 // Undo any currently pressed mouse buttons.
250 for (int i = 1; i < MouseEvent::BUTTON_MAX; i++) {
251 if (remote_mouse_button_state_ & (1 << (i - 1))) {
252 MouseEvent mouse;
[email protected]15e7b6c2011-12-22 10:20:33253 // TODO(wez): Shouldn't [need to] set position here.
[email protected]b67fb9302011-09-26 01:55:52254 mouse.set_x(remote_mouse_pos_.x());
255 mouse.set_y(remote_mouse_pos_.y());
256 mouse.set_button((MouseEvent::MouseButton)i);
257 mouse.set_button_down(false);
258 input_stub_->InjectMouseEvent(mouse);
259 }
260 }
261 remote_mouse_button_state_ = 0;
[email protected]86c5a1e2011-06-29 20:50:15262}
263
[email protected]44f60762011-03-23 12:13:35264} // namespace remoting