text: // Copyright 2015 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "remoting/client/input/normalizing_input_filter_mac.h"
  
  #include <map>
  #include <vector>
  
  #include "base/logging.h"
  #include "remoting/proto/event.pb.h"
  #include "ui/events/keycodes/dom/dom_code.h"
  
  namespace remoting {
  
  NormalizingInputFilterMac::NormalizingInputFilterMac(
      protocol::InputStub* input_stub)
      : protocol::InputFilter(input_stub) {}
  
  NormalizingInputFilterMac::~NormalizingInputFilterMac() = default;
  
  void NormalizingInputFilterMac::InjectKeyEvent(
      const protocol::KeyEvent& event_arg) {
    DCHECK(event_arg.has_usb_keycode());
  
    // Mac OS X doesn't have a concept of num lock, so unset the field.
    protocol::KeyEvent event(event_arg);
    event.clear_num_lock_state();
  
    ui::DomCode dom_code = static_cast<ui::DomCode>(event.usb_keycode());
  
    bool is_special_key = dom_code == ui::DomCode::CONTROL_LEFT ||
                          dom_code == ui::DomCode::SHIFT_LEFT ||
                          dom_code == ui::DomCode::ALT_LEFT ||
                          dom_code == ui::DomCode::CONTROL_RIGHT ||
                          dom_code == ui::DomCode::SHIFT_RIGHT ||
                          dom_code == ui::DomCode::ALT_RIGHT ||
                          dom_code == ui::DomCode::TAB;
  
    bool is_cmd_key =
        dom_code == ui::DomCode::META_LEFT || dom_code == ui::DomCode::META_RIGHT;
  
    if (dom_code == ui::DomCode::CAPS_LOCK) {
      // Mac OS X generates keydown/keyup on lock-state transitions, rather than
      // when the key is pressed & released, so fake keydown/keyup on each event.
      protocol::KeyEvent newEvent(event);
  
      newEvent.set_pressed(true);
      InputFilter::InjectKeyEvent(newEvent);
      newEvent.set_pressed(false);
      InputFilter::InjectKeyEvent(newEvent);
  
      return;
    } else if (!is_cmd_key && !is_special_key) {
      // Track keydown/keyup events for non-modifiers, so we can release them if
      // necessary (see below).
      if (event.pressed()) {
        key_pressed_map_[event.usb_keycode()] = event;
      } else {
        key_pressed_map_.erase(event.usb_keycode());
      }
    }
  
    if (is_cmd_key && !event.pressed()) {
      // Mac OS X will not generate release events for keys pressed while Cmd is
      // pressed, so release all pressed keys when Cmd is released.
      GenerateKeyupEvents();
    }
  
    InputFilter::InjectKeyEvent(event);
  }
  
  void NormalizingInputFilterMac::GenerateKeyupEvents() {
    for (auto i = key_pressed_map_.begin(); i != key_pressed_map_.end(); ++i) {
      // The generated key up event will have the same key code and lock states
      // as the original key down event.
      protocol::KeyEvent event = i->second;
      event.set_pressed(false);
      InputFilter::InjectKeyEvent(event);
    }
  
    // Clearing the map now that we have released all the pressed keys.
    key_pressed_map_.clear();
  }
  
  }  // namespace remoting
  