chromium/ash/display/touch_calibrator_controller.h

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_DISPLAY_TOUCH_CALIBRATOR_CONTROLLER_H_
#define ASH_DISPLAY_TOUCH_CALIBRATOR_CONTROLLER_H_

#include <map>

#include "ash/ash_export.h"
#include "base/gtest_prod_util.h"
#include "base/time/time.h"
#include "ui/display/display.h"
#include "ui/display/display_layout.h"
#include "ui/display/manager/display_manager_observer.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/display/manager/touch_device_manager.h"
#include "ui/events/devices/touchscreen_device.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/views/widget/unique_widget_ptr.h"

namespace ui {
class KeyEvent;
class TouchEvent;
}  // namespace ui

namespace ash {

class TouchCalibratorView;

// TouchCalibratorController is responsible for managing the touch calibration
// process. In case of native touch calibration it is also responsible for
// collecting the touch calibration associated data from the user. It
// instantiates TouchCalibratorView classes to present the native UX interface
// the user can interact with for calibration.
// This controller ensures that only one instance of calibration is running at
// any given time.
class ASH_EXPORT TouchCalibratorController
    : public ui::EventHandler,
      public display::DisplayManagerObserver {
 public:
  using CalibrationPointPairQuad =
      display::TouchCalibrationData::CalibrationPointPairQuad;
  using TouchCalibrationCallback = base::OnceCallback<void(bool)>;

  static const base::TimeDelta kTouchIntervalThreshold;

  TouchCalibratorController();

  TouchCalibratorController(const TouchCalibratorController&) = delete;
  TouchCalibratorController& operator=(const TouchCalibratorController&) =
      delete;

  ~TouchCalibratorController() override;

  // ui::EventHandler
  void OnKeyEvent(ui::KeyEvent* event) override;
  void OnTouchEvent(ui::TouchEvent* event) override;

  // display::DisplayManagerObserver
  void OnDidApplyDisplayChanges() override;

  // Starts the calibration process for the given |target_display|.
  // |opt_callback| is an optional callback that if provided is executed
  // with the success or failure of the calibration as a boolean argument.
  void StartCalibration(const display::Display& target_display,
                        bool is_custom_calibration,
                        TouchCalibrationCallback opt_callback);

  // Maps all monitors to their matching touchscreen device. Provided callback
  // will be called once all displays have been mapped.
  void StartNativeTouchscreenMappingExperience(
      TouchCalibrationCallback opt_callback);

  // Stops any ongoing calibration process. This is a hard stop which does not
  // save any calibration data. Call CompleteCalibration() if you wish to save
  // calibration data.
  void StopCalibrationAndResetParams();

  // Completes the touch calibration by storing the calibration data for the
  // display.
  void CompleteCalibration(const CalibrationPointPairQuad& pairs,
                           const gfx::Size& display_size);

  // Returns true if any type of touch calibration is active.
  bool IsCalibrating() const;

 private:
  friend class TouchCalibratorControllerTest;
  FRIEND_TEST_ALL_PREFIXES(TouchCalibratorControllerTest, TouchThreshold);
  FRIEND_TEST_ALL_PREFIXES(TouchCalibratorControllerTest, CustomCalibration);
  FRIEND_TEST_ALL_PREFIXES(TouchCalibratorControllerTest,
                           CustomCalibrationInvalidTouchId);
  FRIEND_TEST_ALL_PREFIXES(TouchCalibratorControllerTest,
                           InternalTouchDeviceIsRejected);
  FRIEND_TEST_ALL_PREFIXES(TouchCalibratorControllerTest,
                           Mapping_TwoExternalDisplays_FullFlow);
  FRIEND_TEST_ALL_PREFIXES(TouchCalibratorControllerTest,
                           Mapping_TwoExternalDisplays_SkipFirst);

  enum class CalibrationState {
    // Indicates that the touch calibration is currently active with the built
    // in native UX.
    kNativeCalibration = 0,

    // Indicates that the touch calibration is currently active with the built
    // in native UX for all displays.
    kNativeCalibrationTouchscreenMapping,

    // Indicates that the touch calibration is currently active with a custom
    // UX via the extensions API.
    kCustomCalibration,

    // Indicates that touch calibration is currently inactive.
    kInactive
  };

  // Iterates over to run the calibration experience on the next display.
  // Used when running the touchscreen mapping experience.
  void CalibrateNextDisplay();

  CalibrationState state_ = CalibrationState::kInactive;

  // A map for TouchCalibrator view with the key as display id of the display
  // it is present in.
  std::map<int64_t, views::UniqueWidgetPtr> touch_calibrator_widgets_;

  // The display which is being calibrated by the touch calibrator controller.
  // This is valid only if |is_calibrating| is set to true.
  display::Display target_display_;

  // During calibration this stores the timestamp when the previous touch event
  // was received.
  base::Time last_touch_timestamp_;

  // This is populated during calibration, based on the source id of the device
  // the events are originating from.
  int touch_device_id_ = ui::InputDevice::kInvalidId;

  // A set of ids that belong to touch devices associated with the internal
  // display and are of type |ui::InputDeviceType::INPUT_DEVICE_INTERNAL|. This
  // is only valid when |state_| is not |kInactive|.
  std::set<int> internal_touch_device_ids_;

  // An array of Calibration point pairs. This stores all the 4 display and
  // touch input point pairs that will be used for calibration.
  CalibrationPointPairQuad touch_point_quad_;

  // A callback to be called when touch calibration completes when started via
  // `StartCalibration`.
  TouchCalibrationCallback opt_callback_;
  // A callback to be called when the native touch mapping experience has
  // completed. This is started via `StartNativeTouchscreenMappingExperience`.
  TouchCalibrationCallback opt_callback_all_displays_;

  // The list of displays were already mapped to touchscreen devices in the
  // current instantiation of the touchscreen mapping experience.
  base::flat_set<int64_t> already_mapped_display_ids_;

  // The touch device under calibration may be re-associated to another display
  // during calibration. In such a case, the events originating from the touch
  // device are tranformed based on parameters of the previous display it was
  // linked to. We need to undo these transformations before recording the event
  // locations.
  gfx::Transform event_transformer_;
};

}  // namespace ash
#endif  // ASH_DISPLAY_TOUCH_CALIBRATOR_CONTROLLER_H_