Home | History | Annotate | Download | only in plugin
      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
    204