Upstream version 11.39.250.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 the left-hand Alt key.
21 static bool IsLeftAltKey(unsigned int code) {
22   const unsigned int kUsbLeftAltKey = 0x0700e2;
23   return code == kUsbLeftAltKey;
24 }
25
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;
31 }
32
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;
38 }
39
40 // Returns true for codes generated by EventRewriter::Rewrite().
41 static bool IsRewrittenKey(unsigned int code) {
42   return IsRewrittenExtendedKey(code) || IsRewrittenFunctionKey(code);
43 }
44
45 }  // namespace
46
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.
50 //
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
59 //     Start menu.
60 //
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
64 //      State #2.
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
70 //      State #4.
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
73 //      State #1.
74 //    - If a Modified event is received the OSKey keydown is sent and we enter
75 //      State #4.
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.
80 //
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.
91 //
92 // This file must be kept up-to-date with changes to
93 // chrome/browser/chromeos/events/event_rewriter.cc
94
95
96 NormalizingInputFilterCros::NormalizingInputFilterCros(
97     protocol::InputStub* input_stub)
98     : protocol::InputFilter(input_stub),
99       deferred_key_is_rewriting_(false),
100       modifying_key_(0),
101       left_alt_is_pressed_(false) {
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 (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);
127   }
128   InputFilter::InjectMouseEvent(newEvent);
129 }
130
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();
138   }
139
140   // If |event| is a |modifying_key_| repeat then let it pass through.
141   if (modifying_key_ == event.usb_keycode()) {
142     InputFilter::InjectKeyEvent(event);
143     return;
144   }
145
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;
151     return;
152   }
153
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
157   // Modifying mode.
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
161     // rewriting.
162     deferred_key_is_rewriting_ = true;
163   } else {
164     if (deferred_keydown_event_.has_usb_keycode())
165       SwitchRewritingKeyToModifying();
166   }
167
168   if (IsLeftAltKey(event.usb_keycode()))
169     left_alt_is_pressed_ = true;
170
171   InputFilter::InjectKeyEvent(event);
172 }
173
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();
180       return;
181     }
182
183     // If the OSKey hasn't Rewritten anything then treat as Modifying.
184     SwitchRewritingKeyToModifying();
185   }
186
187   if (modifying_key_ == event.usb_keycode())
188     modifying_key_ = 0;
189
190   if (IsLeftAltKey(event.usb_keycode()))
191     left_alt_is_pressed_ = false;
192
193   InputFilter::InjectKeyEvent(event);
194 }
195
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();
201 }
202
203 }  // namespace remoting