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 codes generated by EventRewriter::RewriteFunctionKeys().
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;
27 // Returns true for codes generated by EventRewriter::RewriteExtendedKeys().
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;
34 // Returns true for codes generated by EventRewriter::Rewrite().
35 static bool IsRewrittenKey(unsigned int code) {
36 return IsRewrittenExtendedKey(code) || IsRewrittenFunctionKey(code);
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.
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
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
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
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
68 // - If a Modified event is received the OSKey keydown is sent and we enter
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.
75 // This file must be kept up-to-date with changes to
76 // chrome/browser/ui/ash/event_rewriter.cc
78 NormalizingInputFilterCros::NormalizingInputFilterCros(
79 protocol::InputStub* input_stub)
80 : protocol::InputFilter(input_stub),
81 deferred_key_is_rewriting_(false),
85 NormalizingInputFilterCros::~NormalizingInputFilterCros() {}
87 void NormalizingInputFilterCros::InjectKeyEvent(
88 const protocol::KeyEvent& event) {
89 DCHECK(event.has_usb_keycode());
90 DCHECK(event.has_pressed());
92 if (event.pressed()) {
93 ProcessKeyDown(event);
99 void NormalizingInputFilterCros::InjectMouseEvent(
100 const protocol::MouseEvent& event) {
101 if (deferred_keydown_event_.has_usb_keycode())
102 SwitchRewritingKeyToModifying();
103 InputFilter::InjectMouseEvent(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();
115 // If |event| is a |modifying_key_| repeat then let it pass through.
116 if (modifying_key_ == event.usb_keycode()) {
117 InputFilter::InjectKeyEvent(event);
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;
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
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
137 deferred_key_is_rewriting_ = true;
139 if (deferred_keydown_event_.has_usb_keycode())
140 SwitchRewritingKeyToModifying();
143 InputFilter::InjectKeyEvent(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();
155 // If the OSKey hasn't Rewritten anything then treat as Modifying.
156 SwitchRewritingKeyToModifying();
159 if (modifying_key_ == event.usb_keycode())
162 InputFilter::InjectKeyEvent(event);
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();
172 } // namespace remoting