text: // Copyright 2013 The Flutter Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_KEY_HANDLER_H_
  #define FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_KEY_HANDLER_H_
  
  #include <windows.h>
  #include <deque>
  #include <memory>
  #include <string>
  
  #include "flutter/shell/platform/common/client_wrapper/include/flutter/basic_message_channel.h"
  #include "flutter/shell/platform/windows/keyboard_handler_base.h"
  #include "rapidjson/document.h"
  
  namespace flutter {
  
  // Handles key events.
  //
  // This class detects whether an incoming event is a redispatched one,
  // dispatches native events to delegates and collect their responses,
  // and redispatches events unhandled by Flutter back to the system.
  // See |KeyboardHook| for more information about dispatching.
  //
  // This class owns multiple |KeyboardKeyHandlerDelegate|s, which
  // implements the exact behavior to asynchronously handle events. In
  // reality, this design is only to support sending events through
  // "channel" (RawKeyEvent) and "embedder" (KeyEvent) simultaneously,
  // the former of which shall be removed after the deprecation window
  // of the RawKeyEvent system.
  class KeyboardKeyHandler : public KeyboardHandlerBase {
   public:
    // An interface for concrete definition of how to asynchronously handle key
    // events.
    class KeyboardKeyHandlerDelegate {
     public:
      // Defines how to how to asynchronously handle key events.
      //
      // |KeyboardHook| should invoke |callback| with the response (whether the
      // event is handled) later for exactly once.
      virtual void KeyboardHook(int key,
                                int scancode,
                                int action,
                                char32_t character,
                                bool extended,
                                bool was_down,
                                std::function<void(bool)> callback) = 0;
  
      virtual ~KeyboardKeyHandlerDelegate();
    };
  
    using EventDispatcher =
        std::function<UINT(UINT cInputs, LPINPUT pInputs, int cbSize)>;
  
    // Create a KeyboardKeyHandler and specify where to redispatch events.
    //
    // The |redispatch_event| is typically |SendInput|, but can also be nullptr
    // (for UWP).
    explicit KeyboardKeyHandler(EventDispatcher dispatch_event);
  
    ~KeyboardKeyHandler();
  
    // Add a delegate that handles events received by |KeyboardHook|.
    void AddDelegate(std::unique_ptr<KeyboardKeyHandlerDelegate> delegate);
  
    // Handles a key event.
    //
    // Returns whether this handler claims to handle the event, which is true if
    // and only if the event is a non-synthesized event.
    //
    // Windows requires a synchronous response of whether a key event should be
    // handled, while the query to Flutter is always asynchronous. This is
    // resolved by the "redispatching" algorithm: by default, the response to a
    // fresh event is always always true. The event is then sent to the framework.
    // If the framework later decides not to handle the event, this class will
    // create an identical event and dispatch it to the system, and remember all
    // synthesized events. The fist time an exact event (by |ComputeEventHash|) is
    // received in the future, the new event is considered a synthesized one,
    // causing |KeyboardHook| to return false to fall back to other keyboard
    // handlers.
    //
    // Whether a non-synthesized event is considered handled by the framework is
    // decided by dispatching the event to all delegates, simultaneously,
    // unconditionally, in insertion order, and collecting their responses later.
    // It's not supported to prevent any delegates to process the events, because
    // in reality this will only support 2 hardcoded delegates, and only to
    // continue supporting the legacy API (channel) during the deprecation window,
    // after which the channel delegate should be removed.
    //
    // Inherited from |KeyboardHandlerBase|.
    bool KeyboardHook(int key,
                      int scancode,
                      int action,
                      char32_t character,
                      bool extended,
                      bool was_down) override;
  
   protected:
    size_t RedispatchedCount();
  
   private:
    struct PendingEvent {
      uint32_t key;
      uint8_t scancode;
      uint32_t action;
      char32_t character;
      bool extended;
      bool was_down;
  
      // Self-incrementing ID attached to an event sent to the framework.
      uint64_t sequence_id;
      // The number of delegates that haven't replied.
      size_t unreplied;
      // Whether any replied delegates reported true (handled).
      bool any_handled;
  
      // A value calculated out of critical event information that can be used
      // to identify redispatched events.
      uint64_t hash;
    };
  
    void DispatchEvent(const PendingEvent& event);
  
    // Find an event in the redispatch list that matches the given one.
    //
    // If an matching event is found, removes the matching event from the
    // redispatch list, and returns true. Otherwise, returns false;
    bool RemoveRedispatchedEvent(const PendingEvent& incoming);
    void RedispatchEvent(std::unique_ptr<PendingEvent> event);
    void ResolvePendingEvent(uint64_t sequence_id, bool handled);
  
    std::vector<std::unique_ptr<KeyboardKeyHandlerDelegate>> delegates_;
  
    // The queue of key events that have been sent to the framework but have not
    // yet received a response.
    std::deque<std::unique_ptr<PendingEvent>> pending_responds_;
  
    // The queue of key events that have been redispatched to the system but have
    // not yet been received for a second time.
    std::deque<std::unique_ptr<PendingEvent>> pending_redispatches_;
  
    // The sequence_id attached to the last event sent to the framework.
    uint64_t last_sequence_id_;
  
    // The callback used to dispatch synthesized events.
    EventDispatcher dispatch_event_;
  
    // Whether the last event is a CtrlLeft key down.
    //
    // This is used to resolve a corner case described in |IsKeyDownAltRight|.
    bool last_key_is_ctrl_left_down;
  
    // The scancode of the last met CtrlLeft down.
    //
    // This is used to resolve a corner case described in |IsKeyDownAltRight|.
    uint8_t ctrl_left_scancode;
  
    // Whether a CtrlLeft up should be synthesized upon the next AltRight up.
    //
    // This is used to resolve a corner case described in |IsKeyDownAltRight|.
    bool should_synthesize_ctrl_left_up;
  
    // Calculate a hash based on event data for fast comparison for a redispatched
    // event.
    //
    // This uses event data instead of generating a serial number because
    // information can't be attached to the redispatched events, so it has to be
    // possible to compute an ID from the identifying data in the event when it is
    // received again in order to differentiate between events that are new, and
    // events that have been redispatched.
    //
    // Another alternative would be to compute a checksum from all the data in the
    // event (just compute it over the bytes in the struct, probably skipping
    // timestamps), but the fields used are enough to differentiate them, and
    // since Windows does some processing on the events (coming up with virtual
    // key codes, setting timestamps, etc.), it's not clear that the redispatched
    // events would have the same checksums.
    static uint64_t ComputeEventHash(const PendingEvent& event);
  };
  
  }  // namespace flutter
  
  #endif  // FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_KEY_HANDLER_H_
  