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_mac.h"
6
7#include <map>
8#include <vector>
9
10#include "base/logging.h"
11#include "remoting/proto/event.pb.h"
12
13namespace remoting {
14
15namespace {
16
17const unsigned int kUsbCapsLock     = 0x070039;
18const unsigned int kUsbLeftControl  = 0x0700e0;
19const unsigned int kUsbLeftShift    = 0x0700e1;
20const unsigned int kUsbLeftOption   = 0x0700e2;
21const unsigned int kUsbLeftCmd      = 0x0700e3;
22const unsigned int kUsbRightControl = 0x0700e4;
23const unsigned int kUsbRightShift   = 0x0700e5;
24const unsigned int kUsbRightOption  = 0x0700e6;
25const unsigned int kUsbRightCmd     = 0x0700e7;
26const unsigned int kUsbTab          = 0x07002b;
27
28}  // namespace
29
30NormalizingInputFilterMac::NormalizingInputFilterMac(
31    protocol::InputStub* input_stub)
32    : protocol::InputFilter(input_stub) {
33}
34
35NormalizingInputFilterMac::~NormalizingInputFilterMac() {}
36
37void NormalizingInputFilterMac::InjectKeyEvent(const protocol::KeyEvent& event)
38{
39  DCHECK(event.has_usb_keycode());
40
41  bool is_special_key = event.usb_keycode() == kUsbLeftControl ||
42      event.usb_keycode() == kUsbLeftShift ||
43      event.usb_keycode() == kUsbLeftOption ||
44      event.usb_keycode() == kUsbRightControl ||
45      event.usb_keycode() == kUsbRightShift ||
46      event.usb_keycode() == kUsbRightOption ||
47      event.usb_keycode() == kUsbTab;
48
49  bool is_cmd_key = event.usb_keycode() == kUsbLeftCmd ||
50      event.usb_keycode() == kUsbRightCmd;
51
52  if (event.usb_keycode() == kUsbCapsLock) {
53    // Mac OS X generates keydown/keyup on lock-state transitions, rather than
54    // when the key is pressed & released, so fake keydown/keyup on each event.
55    protocol::KeyEvent newEvent(event);
56
57    newEvent.set_pressed(true);
58    InputFilter::InjectKeyEvent(newEvent);
59    newEvent.set_pressed(false);
60    InputFilter::InjectKeyEvent(newEvent);
61
62    return;
63  } else if (!is_cmd_key && !is_special_key) {
64    // Track keydown/keyup events for non-modifiers, so we can release them if
65    // necessary (see below).
66    if (event.pressed()) {
67      key_pressed_map_[event.usb_keycode()] = event;
68    } else {
69      key_pressed_map_.erase(event.usb_keycode());
70    }
71  }
72
73  if (is_cmd_key && !event.pressed()) {
74    // Mac OS X will not generate release events for keys pressed while Cmd is
75    // pressed, so release all pressed keys when Cmd is released.
76    GenerateKeyupEvents();
77  }
78
79  InputFilter::InjectKeyEvent(event);
80}
81
82void NormalizingInputFilterMac::GenerateKeyupEvents() {
83  for (KeyPressedMap::iterator i = key_pressed_map_.begin();
84       i != key_pressed_map_.end(); ++i) {
85    // The generated key up event will have the same key code and lock states
86    // as the original key down event.
87    protocol::KeyEvent event = i->second;
88    event.set_pressed(false);
89    InputFilter::InjectKeyEvent(event);
90  }
91
92  // Clearing the map now that we have released all the pressed keys.
93  key_pressed_map_.clear();
94}
95
96}  // namespace remoting
97