| 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 |