Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / remoting / client / plugin / normalizing_input_filter_cros.cc
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.
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().
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().
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().
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
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
85 NormalizingInputFilterCros::~NormalizingInputFilterCros() {}
86
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
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
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
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
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