Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(209)

Side by Side Diff: remoting/host/client_session.cc

Issue 9465035: Move ClientSession's input logic into separate components. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Move filtering based on local input to RemoteInputFilter. Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/host/client_session.h" 5 #include "remoting/host/client_session.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/message_loop_proxy.h" 9 #include "base/message_loop_proxy.h"
10 #include "remoting/host/capturer.h" 10 #include "remoting/host/capturer.h"
11 #include "remoting/proto/event.pb.h" 11 #include "remoting/proto/event.pb.h"
12 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.
17 static const unsigned int kNumRemoteMousePositions = 50;
18
19 // The number of milliseconds for which to block remote input when local input
20 // is received.
21 static const int64 kRemoteBlockTimeoutMillis = 2000;
22
23 namespace remoting { 13 namespace remoting {
24 14
25 using protocol::KeyEvent; 15 using protocol::KeyEvent;
26 using protocol::MouseEvent; 16 using protocol::MouseEvent;
27 17
28 ClientSession::ClientSession( 18 ClientSession::ClientSession(
29 EventHandler* event_handler, 19 EventHandler* event_handler,
30 protocol::ConnectionToClient* connection, 20 protocol::ConnectionToClient* connection,
31 protocol::InputStub* input_stub, 21 protocol::InputStub* input_stub,
32 Capturer* capturer) 22 Capturer* capturer)
33 : event_handler_(event_handler), 23 : event_handler_(event_handler),
34 connection_(connection), 24 connection_(connection),
35 client_jid_(connection->session()->jid()), 25 client_jid_(connection->session()->jid()),
36 input_stub_(input_stub), 26 input_tracker_(input_stub),
37 capturer_(capturer), 27 remote_input_filter_(&input_tracker_),
38 authenticated_(false), 28 capturer_(capturer) {
39 awaiting_continue_approval_(false),
40 remote_mouse_button_state_(0) {
41 connection_->SetEventHandler(this); 29 connection_->SetEventHandler(this);
42 30
43 // TODO(sergeyu): Currently ConnectionToClient expects stubs to be 31 // TODO(sergeyu): Currently ConnectionToClient expects stubs to be
44 // set before channels are connected. Make it possible to set stubs 32 // set before channels are connected. Make it possible to set stubs
45 // later and set them only when connection is authenticated. 33 // later and set them only when connection is authenticated.
46 connection_->set_host_stub(this); 34 connection_->set_host_stub(this);
47 connection_->set_input_stub(this); 35 connection_->set_input_stub(&input_filter_);
48 } 36 }
49 37
50 ClientSession::~ClientSession() { 38 ClientSession::~ClientSession() {
51 } 39 }
52 40
53 void ClientSession::InjectKeyEvent(const KeyEvent& event) { 41 void ClientSession::InjectKeyEvent(const KeyEvent& event) {
54 DCHECK(CalledOnValidThread()); 42 DCHECK(CalledOnValidThread());
55 43 input_filter_.InjectKeyEvent(event);
56 if (authenticated_ && !ShouldIgnoreRemoteKeyboardInput(event)) {
57 RecordKeyEvent(event);
58 input_stub_->InjectKeyEvent(event);
59 }
60 } 44 }
61 45
62 void ClientSession::InjectMouseEvent(const MouseEvent& event) { 46 void ClientSession::InjectMouseEvent(const MouseEvent& event) {
63 DCHECK(CalledOnValidThread()); 47 DCHECK(CalledOnValidThread());
64 48
65 if (authenticated_ && !ShouldIgnoreRemoteMouseInput(event)) { 49 MouseEvent event_to_inject = event;
66 RecordMouseButtonState(event); 50 if (event.has_x() && event.has_y()) {
67 MouseEvent event_to_inject = event; 51 // In case the client sends events with off-screen coordinates, modify
68 if (event.has_x() && event.has_y()) { 52 // the event to lie within the current screen area. This is better than
69 // In case the client sends events with off-screen coordinates, modify 53 // simply discarding the event, which might lose a button-up event at the
70 // the event to lie within the current screen area. This is better than 54 // end of a drag'n'drop (or cause other related problems).
71 // simply discarding the event, which might lose a button-up event at the 55 SkIPoint pos(SkIPoint::Make(event.x(), event.y()));
72 // end of a drag'n'drop (or cause other related problems). 56 const SkISize& screen = capturer_->size_most_recent();
73 SkIPoint pos(SkIPoint::Make(event.x(), event.y())); 57 pos.setX(std::max(0, std::min(screen.width() - 1, pos.x())));
74 const SkISize& screen = capturer_->size_most_recent(); 58 pos.setY(std::max(0, std::min(screen.height() - 1, pos.y())));
75 pos.setX(std::max(0, std::min(screen.width() - 1, pos.x()))); 59 event_to_inject.set_x(pos.x());
76 pos.setY(std::max(0, std::min(screen.height() - 1, pos.y()))); 60 event_to_inject.set_y(pos.y());
77 event_to_inject.set_x(pos.x());
78 event_to_inject.set_y(pos.y());
79
80 // 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
85 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();
89 }
90 }
91 input_stub_->InjectMouseEvent(event_to_inject);
92 } 61 }
62 input_filter_.InjectMouseEvent(event_to_inject);
93 } 63 }
94 64
95 void ClientSession::OnConnectionOpened( 65 void ClientSession::OnConnectionOpened(
96 protocol::ConnectionToClient* connection) { 66 protocol::ConnectionToClient* connection) {
97 DCHECK(CalledOnValidThread()); 67 DCHECK(CalledOnValidThread());
98 DCHECK_EQ(connection_.get(), connection); 68 DCHECK_EQ(connection_.get(), connection);
99 authenticated_ = true; 69 SetDisableInputs(false);
100 event_handler_->OnSessionAuthenticated(this); 70 event_handler_->OnSessionAuthenticated(this);
101 } 71 }
102 72
103 void ClientSession::OnConnectionClosed( 73 void ClientSession::OnConnectionClosed(
104 protocol::ConnectionToClient* connection) { 74 protocol::ConnectionToClient* connection) {
105 DCHECK(CalledOnValidThread()); 75 DCHECK(CalledOnValidThread());
106 DCHECK_EQ(connection_.get(), connection); 76 DCHECK_EQ(connection_.get(), connection);
107 event_handler_->OnSessionClosed(this); 77 event_handler_->OnSessionClosed(this);
108 } 78 }
109 79
(...skipping 22 matching lines...) Expand all
132 const net::IPEndPoint& local_end_point) { 102 const net::IPEndPoint& local_end_point) {
133 DCHECK(CalledOnValidThread()); 103 DCHECK(CalledOnValidThread());
134 DCHECK_EQ(connection_.get(), connection); 104 DCHECK_EQ(connection_.get(), connection);
135 event_handler_->OnSessionRouteChange(this, channel_name, remote_end_point, 105 event_handler_->OnSessionRouteChange(this, channel_name, remote_end_point,
136 local_end_point); 106 local_end_point);
137 } 107 }
138 108
139 void ClientSession::Disconnect() { 109 void ClientSession::Disconnect() {
140 DCHECK(CalledOnValidThread()); 110 DCHECK(CalledOnValidThread());
141 DCHECK(connection_.get()); 111 DCHECK(connection_.get());
142 authenticated_ = false; 112
143 RestoreEventState(); 113 // SetDisableInputs(true) implicitly restores the input event state.
114 SetDisableInputs(true);
144 115
145 // This triggers OnSessionClosed() and the session may be destroyed 116 // This triggers OnSessionClosed() and the session may be destroyed
146 // as the result, so this call must be the last in this method. 117 // as the result, so this call must be the last in this method.
147 connection_->Disconnect(); 118 connection_->Disconnect();
148 } 119 }
149 120
150 void ClientSession::LocalMouseMoved(const SkIPoint& mouse_pos) { 121 void ClientSession::LocalMouseMoved(const SkIPoint& mouse_pos) {
151 DCHECK(CalledOnValidThread()); 122 DCHECK(CalledOnValidThread());
123 remote_input_filter_.LocalMouseMoved(mouse_pos);
124 }
152 125
153 // If this is a genuine local input event (rather than an echo of a remote 126 void ClientSession::SetDisableInputs(bool disable_inputs) {
154 // input event that we've just injected), then ignore remote inputs for a 127 DCHECK(CalledOnValidThread());
155 // short time. 128
156 std::list<SkIPoint>::iterator found_position = 129 if (disable_inputs) {
157 std::find(injected_mouse_positions_.begin(), 130 input_filter_.set_input_stub(NULL);
158 injected_mouse_positions_.end(), mouse_pos); 131 input_tracker_.ReleaseAll();
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);
170 } else { 132 } else {
171 latest_local_input_time_ = base::Time::Now(); 133 input_filter_.set_input_stub(&remote_input_filter_);
172 } 134 }
173 } 135 }
174 136
175 bool ClientSession::ShouldIgnoreRemoteMouseInput(
176 const protocol::MouseEvent& event) const {
177 DCHECK(CalledOnValidThread());
178
179 // If the last remote input event was a click or a drag, then it's not safe
180 // 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.
182 if (remote_mouse_button_state_ != 0)
183 return false;
184 // 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.
189 int64 millis = (base::Time::Now() - latest_local_input_time_)
190 .InMilliseconds();
191 if (millis < kRemoteBlockTimeoutMillis)
192 return true;
193 return false;
194 }
195
196 bool ClientSession::ShouldIgnoreRemoteKeyboardInput(
197 const KeyEvent& event) const {
198 DCHECK(CalledOnValidThread());
199
200 // 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_) {
204 return event.pressed() ||
205 (pressed_keys_.find(event.keycode()) == pressed_keys_.end());
206 }
207 return false;
208 }
209
210 void ClientSession::RecordKeyEvent(const KeyEvent& event) {
211 DCHECK(CalledOnValidThread());
212
213 if (event.pressed()) {
214 pressed_keys_.insert(event.keycode());
215 } else {
216 pressed_keys_.erase(event.keycode());
217 }
218 }
219
220 void ClientSession::RecordMouseButtonState(const MouseEvent& event) {
221 DCHECK(CalledOnValidThread());
222
223 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
236 void ClientSession::RestoreEventState() {
237 DCHECK(CalledOnValidThread());
238
239 // Undo any currently pressed keys.
240 std::set<int>::iterator i;
241 for (i = pressed_keys_.begin(); i != pressed_keys_.end(); ++i) {
242 KeyEvent key;
243 key.set_keycode(*i);
244 key.set_pressed(false);
245 input_stub_->InjectKeyEvent(key);
246 }
247 pressed_keys_.clear();
248
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;
253 // TODO(wez): Shouldn't [need to] set position here.
254 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;
262 }
263
264 } // namespace remoting 137 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698