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
3 // found in the LICENSE file.
4 
5 #include "remoting/host/remote_input_filter.h"
6 
7 #include <stdint.h>
8 
9 #include "base/logging.h"
10 #include "remoting/proto/event.pb.h"
11 
12 namespace {
13 
14 // The number of remote mouse events to record for the purpose of eliminating
15 // "echoes" detected by the local input detector. The value should be large
16 // enough to cope with the fact that multiple events might be injected before
17 // any echoes are detected.
18 const unsigned int kNumRemoteMousePositions = 50;
19 
20 // The number of remote keypress events to record for the purpose of eliminating
21 // "echoes" detected by the local input detector. The value should be large
22 // enough to cope with the fact that multiple events might be injected before
23 // any echoes are detected.
24 const unsigned int kNumRemoteKeyPresses = 20;
25 
26 // The number of milliseconds for which to block remote input when local input
27 // is received.
28 const int64_t kRemoteBlockTimeoutMillis = 2000;
29 
30 } // namespace
31 
32 namespace remoting {
33 
RemoteInputFilter(protocol::InputEventTracker * event_tracker)34 RemoteInputFilter::RemoteInputFilter(protocol::InputEventTracker* event_tracker)
35     : event_tracker_(event_tracker),
36       expect_local_echo_(true) {
37 }
38 
39 RemoteInputFilter::~RemoteInputFilter() = default;
40 
LocalPointerMoved(const webrtc::DesktopVector & pos,ui::EventType type)41 bool RemoteInputFilter::LocalPointerMoved(const webrtc::DesktopVector& pos,
42                                           ui::EventType type) {
43   // If this is a genuine local input event (rather than an echo of a remote
44   // input event that we've just injected), then ignore remote inputs for a
45   // short time.
46   //
47   // Note that no platforms both inject and monitor for touch events, so echo
48   // suppression is only applied to mouse input.
49   if (expect_local_echo_ && type == ui::ET_MOUSE_MOVED) {
50     auto found_position = injected_mouse_positions_.begin();
51     while (found_position != injected_mouse_positions_.end() &&
52            !pos.equals(*found_position)) {
53       ++found_position;
54     }
55     if (found_position != injected_mouse_positions_.end()) {
56       // Remove it from the list, and any positions that were added before it,
57       // if any.  This is because the local input monitor is assumed to receive
58       // injected mouse position events in the order in which they were injected
59       // (if at all).  If the position is found somewhere other than the front
60       // of the queue, this would be because the earlier positions weren't
61       // successfully injected (or the local input monitor might have skipped
62       // over some positions), and not because the events were out-of-sequence.
63       // These spurious positions should therefore be discarded.
64       injected_mouse_positions_.erase(injected_mouse_positions_.begin(),
65                                       ++found_position);
66       return false;
67     }
68   }
69 
70   LocalInputDetected();
71   return true;
72 }
73 
LocalKeyPressed(uint32_t usb_keycode)74 bool RemoteInputFilter::LocalKeyPressed(uint32_t usb_keycode) {
75   // If local echo is expected and |usb_keycode| is the oldest unechoed injected
76   // keypress, then ignore it.
77   if (expect_local_echo_ && !injected_key_presses_.empty() &&
78       injected_key_presses_.front() == usb_keycode) {
79     injected_key_presses_.pop_front();
80     return false;
81   }
82 
83   LocalInputDetected();
84   return true;
85 }
86 
LocalInputDetected()87 void RemoteInputFilter::LocalInputDetected() {
88   event_tracker_->ReleaseAll();
89   latest_local_input_time_ = base::TimeTicks::Now();
90 }
91 
SetExpectLocalEcho(bool expect_local_echo)92 void RemoteInputFilter::SetExpectLocalEcho(bool expect_local_echo) {
93   expect_local_echo_ = expect_local_echo;
94   if (!expect_local_echo_)
95     injected_mouse_positions_.clear();
96 }
97 
InjectKeyEvent(const protocol::KeyEvent & event)98 void RemoteInputFilter::InjectKeyEvent(const protocol::KeyEvent& event) {
99   if (ShouldIgnoreInput())
100     return;
101   if (expect_local_echo_ && event.pressed() && event.has_usb_keycode()) {
102     injected_key_presses_.push_back(event.usb_keycode());
103     if (injected_key_presses_.size() > kNumRemoteKeyPresses) {
104       VLOG(1) << "Injected key press queue full.";
105       injected_key_presses_.clear();
106     }
107   }
108   event_tracker_->InjectKeyEvent(event);
109 }
110 
InjectTextEvent(const protocol::TextEvent & event)111 void RemoteInputFilter::InjectTextEvent(const protocol::TextEvent& event) {
112   if (ShouldIgnoreInput())
113     return;
114   event_tracker_->InjectTextEvent(event);
115 }
116 
InjectMouseEvent(const protocol::MouseEvent & event)117 void RemoteInputFilter::InjectMouseEvent(const protocol::MouseEvent& event) {
118   if (ShouldIgnoreInput())
119     return;
120   if (expect_local_echo_ && event.has_x() && event.has_y()) {
121     injected_mouse_positions_.push_back(
122         webrtc::DesktopVector(event.x(), event.y()));
123     if (injected_mouse_positions_.size() > kNumRemoteMousePositions) {
124       VLOG(1) << "Injected mouse positions queue full.";
125       injected_mouse_positions_.pop_front();
126     }
127   }
128   event_tracker_->InjectMouseEvent(event);
129 }
130 
InjectTouchEvent(const protocol::TouchEvent & event)131 void RemoteInputFilter::InjectTouchEvent(const protocol::TouchEvent& event) {
132   if (ShouldIgnoreInput())
133     return;
134   event_tracker_->InjectTouchEvent(event);
135 }
136 
ShouldIgnoreInput() const137 bool RemoteInputFilter::ShouldIgnoreInput() const {
138   // Ignore remote events if the local mouse moved recently.
139   int64_t millis =
140       (base::TimeTicks::Now() - latest_local_input_time_).InMilliseconds();
141   if (millis < kRemoteBlockTimeoutMillis)
142     return true;
143   return false;
144 }
145 
146 }  // namespace remoting
147