OLD | NEW |
| (Empty) |
1 // Copyright 2013 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/client/plugin/normalizing_input_filter_cros.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "remoting/protocol/usb_key_codes.h" | |
9 | |
10 namespace remoting { | |
11 | |
12 namespace { | |
13 | |
14 // Returns true for OSKey codes. | |
15 static bool IsOsKey(unsigned int code) { | |
16 return code == kUsbLeftOs || code == kUsbRightOs; | |
17 } | |
18 | |
19 // Returns true for the left-hand Alt key. | |
20 static bool IsLeftAltKey(unsigned int code) { | |
21 return code == kUsbLeftAlt; | |
22 } | |
23 | |
24 // Returns true for codes generated by EventRewriter::RewriteFunctionKeys(). | |
25 static bool IsRewrittenFunctionKey(unsigned int code) { | |
26 const unsigned int kUsbFunctionKeyMin = 0x07003a; | |
27 const unsigned int kUsbFunctionKeyMax = 0x070045; | |
28 return code >= kUsbFunctionKeyMin && code <= kUsbFunctionKeyMax; | |
29 } | |
30 | |
31 // Returns true for codes generated by EventRewriter::RewriteExtendedKeys(). | |
32 static bool IsRewrittenExtendedKey(unsigned int code) { | |
33 const unsigned int kUsbExtendedKeyMin = 0x070049; | |
34 const unsigned int kUsbExtendedKeyMax = 0x07004e; | |
35 return code >= kUsbExtendedKeyMin && code <= kUsbExtendedKeyMax; | |
36 } | |
37 | |
38 // Returns true for codes generated by EventRewriter::Rewrite(). | |
39 static bool IsRewrittenKey(unsigned int code) { | |
40 return IsRewrittenExtendedKey(code) || IsRewrittenFunctionKey(code); | |
41 } | |
42 | |
43 } // namespace | |
44 | |
45 // The input filter tries to avoid sending keydown/keyup events for OSKey | |
46 // (aka Search, WinKey, Cmd, Super) when it is used to rewrite other key events. | |
47 // Rewriting via other combinations is not currently handled. | |
48 // | |
49 // OSKey events can be categorised as one of three kinds: | |
50 // - Modifying - Holding the key down while executing other input modifies the | |
51 // effect of that input, e.g. OSKey+L causes the workstation to lock, e.g. | |
52 // OSKey + mouse-move performs an extended selection. | |
53 // - Rewriting (ChromeOS only) - Holding the key down while pressing certain | |
54 // keys causes them to be treated as different keys, e.g. OSKey causes the | |
55 // Down key to behave as PageDown. | |
56 // - Normal - Press & release of the key trigger an action, e.g. showing the | |
57 // Start menu. | |
58 // | |
59 // The input filter has four states: | |
60 // 1. No OSKey has been pressed. | |
61 // - When an OSKey keydown is received, the event is deferred, and we move to | |
62 // State #2. | |
63 // 2. An OSKey is pressed, but may be Normal, Rewriting or Modifying. | |
64 // - If the OSKey keyup is received, the key is Normal, both events are sent | |
65 // and we return to State #1. | |
66 // - If a Rewritten event is received we move to State #3. | |
67 // - If a Modified event is received the OSKey keydown is sent and we enter | |
68 // State #4. | |
69 // 3. An OSKey is pressed, and is being used to Rewrite other key events. | |
70 // - If the OSKey keyup is received then it is suppressed, and we move to | |
71 // State #1. | |
72 // - If a Modified event is received the OSKey keydown is sent and we enter | |
73 // State #4. | |
74 // - If a Rewritten event is received then we stay in State #3. | |
75 // 4. An OSKey is pressed, and is Modifying. | |
76 // - If the OSKey keyup is received then we send it and we move to State #1. | |
77 // - All other key event pass through the filter unchanged. | |
78 // | |
79 // ChromeOS also maps Alt+LeftClick to RightClick (even for an external mouse). | |
80 // As with the OSKey remapping described above, this is fed into this filter | |
81 // as Alt followed by RightClick. However, because there are other ways to | |
82 // generate RightClick (two-finger tap, for example), rather than suppressing | |
83 // the Alt key as we do for the OSKey (which would allow Alt+LeftClick to be | |
84 // interpreted as interpreted as RightClick as per the ChromeOS idiom), the | |
85 // filter maps RightClick to LeftClick while LeftAlt is held, which allows | |
86 // Alt+LeftClick to be injected. The equivalent mapping using RightAlt is | |
87 // unchanged, allowing Alt+RightClick also to be injected, as long as the | |
88 // target application doesn't distinguish between left and right Alt keys. | |
89 // | |
90 // This file must be kept up-to-date with changes to | |
91 // chrome/browser/chromeos/events/event_rewriter.cc | |
92 | |
93 | |
94 NormalizingInputFilterCros::NormalizingInputFilterCros( | |
95 protocol::InputStub* input_stub) | |
96 : protocol::InputFilter(input_stub), | |
97 deferred_key_is_rewriting_(false), | |
98 modifying_key_(0), | |
99 left_alt_is_pressed_(false), | |
100 previous_mouse_x_(-1), | |
101 previous_mouse_y_(-1) { | |
102 } | |
103 | |
104 NormalizingInputFilterCros::~NormalizingInputFilterCros() {} | |
105 | |
106 void NormalizingInputFilterCros::InjectKeyEvent( | |
107 const protocol::KeyEvent& event) { | |
108 DCHECK(event.has_usb_keycode()); | |
109 DCHECK(event.has_pressed()); | |
110 | |
111 if (event.pressed()) { | |
112 ProcessKeyDown(event); | |
113 } else { | |
114 ProcessKeyUp(event); | |
115 } | |
116 } | |
117 | |
118 void NormalizingInputFilterCros::InjectMouseEvent( | |
119 const protocol::MouseEvent& event) { | |
120 // If there's a rewriting/modifier decision pending, assume that it's | |
121 // intended to be used as a modifying key for this mouse event and send it. | |
122 if (deferred_keydown_event_.has_usb_keycode()) { | |
123 // TODO(jamiewalch): Until crbug.com/489468 is fixed, a spurious mouse move | |
124 // event is generated in response to certain key combinations, so check that | |
125 // this is actually an "interesting" event. | |
126 if (event.has_button() || | |
127 event.x() != previous_mouse_x_ || | |
128 event.y() != previous_mouse_y_) { | |
129 SwitchRewritingKeyToModifying(); | |
130 } | |
131 } | |
132 previous_mouse_x_ = event.x(); | |
133 previous_mouse_y_ = event.y(); | |
134 | |
135 protocol::MouseEvent newEvent = event; | |
136 if (left_alt_is_pressed_ && | |
137 event.has_button() && | |
138 event.button() == protocol::MouseEvent::BUTTON_RIGHT) { | |
139 newEvent.set_button(protocol::MouseEvent::BUTTON_LEFT); | |
140 } | |
141 InputFilter::InjectMouseEvent(newEvent); | |
142 } | |
143 | |
144 void NormalizingInputFilterCros::ProcessKeyDown( | |
145 const protocol::KeyEvent& event) { | |
146 // If |event| is |deferred_keydown_event_| repeat then assume that the user is | |
147 // holding the key down rather than using it to Rewrite. | |
148 if (deferred_keydown_event_.has_usb_keycode() && | |
149 deferred_keydown_event_.usb_keycode() == event.usb_keycode()) { | |
150 SwitchRewritingKeyToModifying(); | |
151 } | |
152 | |
153 // If |event| is a |modifying_key_| repeat then let it pass through. | |
154 if (modifying_key_ == event.usb_keycode()) { | |
155 InputFilter::InjectKeyEvent(event); | |
156 return; | |
157 } | |
158 | |
159 // If |event| is for an OSKey and we don't know whether it's a Normal, | |
160 // Rewriting or Modifying use, then hold the keydown event. | |
161 if (IsOsKey(event.usb_keycode())) { | |
162 deferred_keydown_event_ = event; | |
163 deferred_key_is_rewriting_ = false; | |
164 return; | |
165 } | |
166 | |
167 // If |event| is for a Rewritten key then set a flag to prevent any deferred | |
168 // OSKey keydown from being sent when keyup is received for it. Otherwise, | |
169 // inject the deferred OSKey keydown, if any, and switch that key into | |
170 // Modifying mode. | |
171 if (IsRewrittenKey(event.usb_keycode())) { | |
172 // Note that there may not be a deferred OSKey event if there is a full | |
173 // PC keyboard connected, which can generate e.g. PageDown without | |
174 // rewriting. | |
175 deferred_key_is_rewriting_ = true; | |
176 } else { | |
177 if (deferred_keydown_event_.has_usb_keycode()) | |
178 SwitchRewritingKeyToModifying(); | |
179 } | |
180 | |
181 if (IsLeftAltKey(event.usb_keycode())) | |
182 left_alt_is_pressed_ = true; | |
183 | |
184 InputFilter::InjectKeyEvent(event); | |
185 } | |
186 | |
187 void NormalizingInputFilterCros::ProcessKeyUp(const protocol::KeyEvent& event) { | |
188 if (deferred_keydown_event_.has_usb_keycode() && | |
189 deferred_keydown_event_.usb_keycode() == event.usb_keycode()) { | |
190 if (deferred_key_is_rewriting_) { | |
191 // If we never sent the keydown then don't send a keyup. | |
192 deferred_keydown_event_ = protocol::KeyEvent(); | |
193 return; | |
194 } | |
195 | |
196 // If the OSKey hasn't Rewritten anything then treat as Modifying. | |
197 SwitchRewritingKeyToModifying(); | |
198 } | |
199 | |
200 if (modifying_key_ == event.usb_keycode()) | |
201 modifying_key_ = 0; | |
202 | |
203 if (IsLeftAltKey(event.usb_keycode())) | |
204 left_alt_is_pressed_ = false; | |
205 | |
206 InputFilter::InjectKeyEvent(event); | |
207 } | |
208 | |
209 void NormalizingInputFilterCros::SwitchRewritingKeyToModifying() { | |
210 DCHECK(deferred_keydown_event_.has_usb_keycode()); | |
211 modifying_key_ = deferred_keydown_event_.usb_keycode(); | |
212 InputFilter::InjectKeyEvent(deferred_keydown_event_); | |
213 deferred_keydown_event_ = protocol::KeyEvent(); | |
214 } | |
215 | |
216 } // namespace remoting | |
OLD | NEW |