normalizing_input_filter_cros.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 9namespace remoting { 10 11namespace { 12 13// Returns true for OSKey codes. 14static 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(). 21static 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(). 28static 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(). 35static 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 78NormalizingInputFilterCros::NormalizingInputFilterCros( 79 protocol::InputStub* input_stub) 80 : protocol::InputFilter(input_stub), 81 deferred_key_is_rewriting_(false), 82 modifying_key_(0) { 83} 84 85NormalizingInputFilterCros::~NormalizingInputFilterCros() {} 86 87void 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 99void NormalizingInputFilterCros::InjectMouseEvent( 100 const protocol::MouseEvent& event) { 101 if (deferred_keydown_event_.has_usb_keycode()) 102 SwitchRewritingKeyToModifying(); 103 InputFilter::InjectMouseEvent(event); 104} 105 106void 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 146void 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 165void 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