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.
5 #include "remoting/client/plugin/normalizing_input_filter_cros.h"
7 #include "base/logging.h"
13 // Returns true for OSKey codes.
14 static bool IsOsKey(unsigned int code) {
15 const unsigned int kUsbLeftOsKey = 0x0700e3;
16 const unsigned int kUsbRightOsKey = 0x0700e7;
17 return code == kUsbLeftOsKey || code == kUsbRightOsKey;
20 // Returns true for the left-hand Alt key.
21 static bool IsLeftAltKey(unsigned int code) {
22 const unsigned int kUsbLeftAltKey = 0x0700e2;
23 return code == kUsbLeftAltKey;
26 // Returns true for codes generated by EventRewriter::RewriteFunctionKeys().
27 static bool IsRewrittenFunctionKey(unsigned int code) {
28 const unsigned int kUsbFunctionKeyMin = 0x07003a;
29 const unsigned int kUsbFunctionKeyMax = 0x070045;
30 return code >= kUsbFunctionKeyMin && code <= kUsbFunctionKeyMax;
33 // Returns true for codes generated by EventRewriter::RewriteExtendedKeys().
34 static bool IsRewrittenExtendedKey(unsigned int code) {
35 const unsigned int kUsbExtendedKeyMin = 0x070049;
36 const unsigned int kUsbExtendedKeyMax = 0x07004e;
37 return code >= kUsbExtendedKeyMin && code <= kUsbExtendedKeyMax;
40 // Returns true for codes generated by EventRewriter::Rewrite().
41 static bool IsRewrittenKey(unsigned int code) {
42 return IsRewrittenExtendedKey(code) || IsRewrittenFunctionKey(code);
47 // The input filter tries to avoid sending keydown/keyup events for OSKey
48 // (aka Search, WinKey, Cmd, Super) when it is used to rewrite other key events.
49 // Rewriting via other combinations is not currently handled.
51 // OSKey events can be categorised as one of three kinds:
52 // - Modifying - Holding the key down while executing other input modifies the
53 // effect of that input, e.g. OSKey+L causes the workstation to lock, e.g.
54 // OSKey + mouse-move performs an extended selection.
55 // - Rewriting (ChromeOS only) - Holding the key down while pressing certain
56 // keys causes them to be treated as different keys, e.g. OSKey causes the
57 // Down key to behave as PageDown.
58 // - Normal - Press & release of the key trigger an action, e.g. showing the
61 // The input filter has four states:
62 // 1. No OSKey has been pressed.
63 // - When an OSKey keydown is received, the event is deferred, and we move to
65 // 2. An OSKey is pressed, but may be Normal, Rewriting or Modifying.
66 // - If the OSKey keyup is received, the key is Normal, both events are sent
67 // and we return to State #1.
68 // - If a Rewritten event is received we move to State #3.
69 // - If a Modified event is received the OSKey keydown is sent and we enter
71 // 3. An OSKey is pressed, and is being used to Rewrite other key events.
72 // - If the OSKey keyup is received then it is suppressed, and we move to
74 // - If a Modified event is received the OSKey keydown is sent and we enter
76 // - If a Rewritten event is received then we stay in State #3.
77 // 4. An OSKey is pressed, and is Modifying.
78 // - If the OSKey keyup is received then we send it and we move to State #1.
79 // - All other key event pass through the filter unchanged.
81 // ChromeOS also maps Alt+LeftClick to RightClick (even for an external mouse).
82 // As with the OSKey remapping described above, this is fed into this filter
83 // as Alt followed by RightClick. However, because there are other ways to
84 // generate RightClick (two-finger tap, for example), rather than suppressing
85 // the Alt key as we do for the OSKey (which would allow Alt+LeftClick to be
86 // interpreted as interpreted as RightClick as per the ChromeOS idiom), the
87 // filter maps RightClick to LeftClick while LeftAlt is held, which allows
88 // Alt+LeftClick to be injected. The equivalent mapping using RightAlt is
89 // unchanged, allowing Alt+RightClick also to be injected, as long as the
90 // target application doesn't distinguish between left and right Alt keys.
92 // This file must be kept up-to-date with changes to
93 // chrome/browser/chromeos/events/event_rewriter.cc
96 NormalizingInputFilterCros::NormalizingInputFilterCros(
97 protocol::InputStub* input_stub)
98 : protocol::InputFilter(input_stub),
99 deferred_key_is_rewriting_(false),
101 left_alt_is_pressed_(false) {
104 NormalizingInputFilterCros::~NormalizingInputFilterCros() {}
106 void NormalizingInputFilterCros::InjectKeyEvent(
107 const protocol::KeyEvent& event) {
108 DCHECK(event.has_usb_keycode());
109 DCHECK(event.has_pressed());
111 if (event.pressed()) {
112 ProcessKeyDown(event);
118 void NormalizingInputFilterCros::InjectMouseEvent(
119 const protocol::MouseEvent& event) {
120 if (deferred_keydown_event_.has_usb_keycode())
121 SwitchRewritingKeyToModifying();
122 protocol::MouseEvent newEvent = event;
123 if (left_alt_is_pressed_ &&
124 event.has_button() &&
125 event.button() == protocol::MouseEvent::BUTTON_RIGHT) {
126 newEvent.set_button(protocol::MouseEvent::BUTTON_LEFT);
128 InputFilter::InjectMouseEvent(newEvent);
131 void NormalizingInputFilterCros::ProcessKeyDown(
132 const protocol::KeyEvent& event) {
133 // If |event| is |deferred_keydown_event_| repeat then assume that the user is
134 // holding the key down rather than using it to Rewrite.
135 if (deferred_keydown_event_.has_usb_keycode() &&
136 deferred_keydown_event_.usb_keycode() == event.usb_keycode()) {
137 SwitchRewritingKeyToModifying();
140 // If |event| is a |modifying_key_| repeat then let it pass through.
141 if (modifying_key_ == event.usb_keycode()) {
142 InputFilter::InjectKeyEvent(event);
146 // If |event| is for an OSKey and we don't know whether it's a Normal,
147 // Rewriting or Modifying use, then hold the keydown event.
148 if (IsOsKey(event.usb_keycode())) {
149 deferred_keydown_event_ = event;
150 deferred_key_is_rewriting_ = false;
154 // If |event| is for a Rewritten key then set a flag to prevent any deferred
155 // OSKey keydown from being sent when keyup is received for it. Otherwise,
156 // inject the deferred OSKey keydown, if any, and switch that key into
158 if (IsRewrittenKey(event.usb_keycode())) {
159 // Note that there may not be a deferred OSKey event if there is a full
160 // PC keyboard connected, which can generate e.g. PageDown without
162 deferred_key_is_rewriting_ = true;
164 if (deferred_keydown_event_.has_usb_keycode())
165 SwitchRewritingKeyToModifying();
168 if (IsLeftAltKey(event.usb_keycode()))
169 left_alt_is_pressed_ = true;
171 InputFilter::InjectKeyEvent(event);
174 void NormalizingInputFilterCros::ProcessKeyUp(const protocol::KeyEvent& event) {
175 if (deferred_keydown_event_.has_usb_keycode() &&
176 deferred_keydown_event_.usb_keycode() == event.usb_keycode()) {
177 if (deferred_key_is_rewriting_) {
178 // If we never sent the keydown then don't send a keyup.
179 deferred_keydown_event_ = protocol::KeyEvent();
183 // If the OSKey hasn't Rewritten anything then treat as Modifying.
184 SwitchRewritingKeyToModifying();
187 if (modifying_key_ == event.usb_keycode())
190 if (IsLeftAltKey(event.usb_keycode()))
191 left_alt_is_pressed_ = false;
193 InputFilter::InjectKeyEvent(event);
196 void NormalizingInputFilterCros::SwitchRewritingKeyToModifying() {
197 DCHECK(deferred_keydown_event_.has_usb_keycode());
198 modifying_key_ = deferred_keydown_event_.usb_keycode();
199 InputFilter::InjectKeyEvent(deferred_keydown_event_);
200 deferred_keydown_event_ = protocol::KeyEvent();
203 } // namespace remoting