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
9 namespace remoting {
10
11 namespace {
12
13 // Returns true for OSKey codes.
IsOsKey(unsigned int code)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;
18 }
19
20 // Returns true for codes generated by EventRewriter::RewriteFunctionKeys().
IsRewrittenFunctionKey(unsigned int code)21 static bool IsRewrittenFunctionKey(unsigned int code) {
22 const unsigned int kUsbFunctionKeyMin = 0x07003a;
23 const unsigned int kUsbFunctionKeyMax = 0x070045;
24 return code >= kUsbFunctionKeyMin && code <= kUsbFunctionKeyMax;
25 }
26
27 // Returns true for codes generated by EventRewriter::RewriteExtendedKeys().
IsRewrittenExtendedKey(unsigned int code)28 static bool IsRewrittenExtendedKey(unsigned int code) {
29 const unsigned int kUsbExtendedKeyMin = 0x070049;
30 const unsigned int kUsbExtendedKeyMax = 0x07004e;
31 return code >= kUsbExtendedKeyMin && code <= kUsbExtendedKeyMax;
32 }
33
34 // Returns true for codes generated by EventRewriter::Rewrite().
IsRewrittenKey(unsigned int code)35 static bool IsRewrittenKey(unsigned int code) {
36 return IsRewrittenExtendedKey(code) || IsRewrittenFunctionKey(code);
37 }
38
39 } // namespace
40
41 // The input filter tries to avoid sending keydown/keyup events for OSKey
42 // (aka Search, WinKey, Cmd, Super) when it is used to rewrite other key events.
43 // Rewriting via other combinations is not currently handled.
44 //
45 // OSKey events can be categorised as one of three kinds:
46 // - Modifying - Holding the key down while executing other input modifies the
47 // effect of that input, e.g. OSKey+L causes the workstation to lock, e.g.
48 // OSKey + mouse-move performs an extended selection.
49 // - Rewriting (ChromeOS only) - Holding the key down while pressing certain
50 // keys causes them to be treated as different keys, e.g. OSKey causes the
51 // Down key to behave as PageDown.
52 // - Normal - Press & release of the key trigger an action, e.g. showing the
53 // Start menu.
54 //
55 // The input filter has four states:
56 // 1. No OSKey has been pressed.
57 // - When an OSKey keydown is received, the event is deferred, and we move to
58 // State #2.
59 // 2. An OSKey is pressed, but may be Normal, Rewriting or Modifying.
60 // - If the OSKey keyup is received, the key is Normal, both events are sent
61 // and we return to State #1.
62 // - If a Rewritten event is received we move to State #3.
63 // - If a Modified event is received the OSKey keydown is sent and we enter
64 // State #4.
65 // 3. An OSKey is pressed, and is being used to Rewrite other key events.
66 // - If the OSKey keyup is received then it is suppressed, and we move to
67 // State #1.
68 // - If a Modified event is received the OSKey keydown is sent and we enter
69 // State #4.
70 // - If a Rewritten event is received then we stay in State #3.
71 // 4. An OSKey is pressed, and is Modifying.
72 // - If the OSKey keyup is received then we send it and we move to State #1.
73 // - All other key event pass through the filter unchanged.
74 //
75 // This file must be kept up-to-date with changes to
76 // chrome/browser/ui/ash/event_rewriter.cc
77
NormalizingInputFilterCros(protocol::InputStub * input_stub)78 NormalizingInputFilterCros::NormalizingInputFilterCros(
79 protocol::InputStub* input_stub)
80 : protocol::InputFilter(input_stub),
81 deferred_key_is_rewriting_(false),
82 modifying_key_(0) {
83 }
84
~NormalizingInputFilterCros()85 NormalizingInputFilterCros::~NormalizingInputFilterCros() {}
86
InjectKeyEvent(const protocol::KeyEvent & event)87 void NormalizingInputFilterCros::InjectKeyEvent(
88 const protocol::KeyEvent& event) {
89 DCHECK(event.has_usb_keycode());
90 DCHECK(event.has_pressed());
91
92 if (event.pressed()) {
93 ProcessKeyDown(event);
94 } else {
95 ProcessKeyUp(event);
96 }
97 }
98
InjectMouseEvent(const protocol::MouseEvent & event)99 void NormalizingInputFilterCros::InjectMouseEvent(
100 const protocol::MouseEvent& event) {
101 if (deferred_keydown_event_.has_usb_keycode())
102 SwitchRewritingKeyToModifying();
103 InputFilter::InjectMouseEvent(event);
104 }
105
ProcessKeyDown(const protocol::KeyEvent & event)106 void NormalizingInputFilterCros::ProcessKeyDown(
107 const protocol::KeyEvent& event) {
108 // If |event| is |deferred_keydown_event_| repeat then assume that the user is
109 // holding the key down rather than using it to Rewrite.
110 if (deferred_keydown_event_.has_usb_keycode() &&
111 deferred_keydown_event_.usb_keycode() == event.usb_keycode()) {
112 SwitchRewritingKeyToModifying();
113 }
114
115 // If |event| is a |modifying_key_| repeat then let it pass through.
116 if (modifying_key_ == event.usb_keycode()) {
117 InputFilter::InjectKeyEvent(event);
118 return;
119 }
120
121 // If |event| is for an OSKey and we don't know whether it's a Normal,
122 // Rewriting or Modifying use, then hold the keydown event.
123 if (IsOsKey(event.usb_keycode())) {
124 deferred_keydown_event_ = event;
125 deferred_key_is_rewriting_ = false;
126 return;
127 }
128
129 // If |event| is for a Rewritten key then set a flag to prevent any deferred
130 // OSKey keydown from being sent when keyup is received for it. Otherwise,
131 // inject the deferred OSKey keydown, if any, and switch that key into
132 // Modifying mode.
133 if (IsRewrittenKey(event.usb_keycode())) {
134 // Note that there may not be a deferred OSKey event if there is a full
135 // PC keyboard connected, which can generate e.g. PageDown without
136 // rewriting.
137 deferred_key_is_rewriting_ = true;
138 } else {
139 if (deferred_keydown_event_.has_usb_keycode())
140 SwitchRewritingKeyToModifying();
141 }
142
143 InputFilter::InjectKeyEvent(event);
144 }
145
ProcessKeyUp(const protocol::KeyEvent & event)146 void NormalizingInputFilterCros::ProcessKeyUp(const protocol::KeyEvent& event) {
147 if (deferred_keydown_event_.has_usb_keycode() &&
148 deferred_keydown_event_.usb_keycode() == event.usb_keycode()) {
149 if (deferred_key_is_rewriting_) {
150 // If we never sent the keydown then don't send a keyup.
151 deferred_keydown_event_ = protocol::KeyEvent();
152 return;
153 }
154
155 // If the OSKey hasn't Rewritten anything then treat as Modifying.
156 SwitchRewritingKeyToModifying();
157 }
158
159 if (modifying_key_ == event.usb_keycode())
160 modifying_key_ = 0;
161
162 InputFilter::InjectKeyEvent(event);
163 }
164
SwitchRewritingKeyToModifying()165 void NormalizingInputFilterCros::SwitchRewritingKeyToModifying() {
166 DCHECK(deferred_keydown_event_.has_usb_keycode());
167 modifying_key_ = deferred_keydown_event_.usb_keycode();
168 InputFilter::InjectKeyEvent(deferred_keydown_event_);
169 deferred_keydown_event_ = protocol::KeyEvent();
170 }
171
172 } // namespace remoting
173