1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
|
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_TOUCH_INJECTOR_H_
#define CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_TOUCH_INJECTOR_H_
#include <memory>
#include <optional>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "base/values.h"
#include "chrome/browser/ash/arc/input_overlay/constants.h"
#include "chrome/browser/ash/arc/input_overlay/db/proto/app_data.pb.h"
#include "ui/events/event_rewriter.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace aura {
class Window;
} // namespace aura
namespace gfx {
class Rect;
class RectF;
class Vector2dF;
} // namespace gfx
namespace ui {
class EventSource;
} // namespace ui
namespace arc::input_overlay {
class Action;
class ArcInputOverlayManagerTest;
class DisplayOverlayController;
class InputElement;
class TouchInjectorObserver;
// If the following touch move sent immediately, the touch move event is not
// processed correctly by apps. This is a delayed time to send touch move
// event.
inline constexpr base::TimeDelta kSendTouchMoveDelay = base::Milliseconds(50);
gfx::RectF CalculateWindowContentBounds(aura::Window* window);
// Maximum default action ID. User-added actions have ID > kMaxDefaultActionID.
inline constexpr int kMaxDefaultActionID = 9999;
// TouchInjector includes all the touch actions related to the specific window
// and performs as a bridge between the ArcInputOverlayManager and the touch
// actions. It implements EventRewriter to transform input events to touch
// events.
class TouchInjector : public ui::EventRewriter {
public:
using OnSaveProtoFileCallback =
base::RepeatingCallback<void(std::unique_ptr<AppDataProto>, std::string)>;
TouchInjector(aura::Window* top_level_window,
const std::string& package_name,
OnSaveProtoFileCallback save_file_callback);
TouchInjector(const TouchInjector&) = delete;
TouchInjector& operator=(const TouchInjector&) = delete;
~TouchInjector() override;
// Parse Json to actions.
// Json value format:
// {
// "tap": [
// {},
// ...
// ],
// "move": [
// {},
// ...
// ]
// }
void ParseActions(const base::Value::Dict& root);
// Update the flags after loading data finished. `is_o4c` is true if the game
// is optimized for ChromeOS.
void UpdateFlags(bool is_o4c);
// Notify the EventRewriter whether the text input is focused or not.
void NotifyTextInputState(bool active);
// Register the EventRewriter.
void RegisterEventRewriter();
// Unregister the EventRewriter.
void UnRegisterEventRewriter();
// Change bindings. This could be from user editing from display overlay
// (`mode` = DisplayMode::kEdit) or from customized protobuf data (`mode` =
// DisplayMode::kView).
void OnInputBindingChange(Action* target_action,
std::unique_ptr<InputElement> input_element);
// Save customized input binding/pending binding as current binding and go
// back from edit mode to view mode.
void OnBindingSave();
void OnProtoDataAvailable(AppDataProto& proto);
// Save proto file.
void OnSaveProtoFile();
// Save the input menu state when the menu is closed.
void OnInputMenuViewRemoved();
void MaybeBindDefaultInputElement(Action* action);
// Update `content_bounds_f_` and touch positions for each `actions_` for
// different reasons.
void UpdatePositionsForRegister();
void UpdateForOverlayBoundsChanged(const gfx::RectF& new_bounds);
// Returns the smallest unused ID (> kMaxDefaultActionID) for adding a new
// action.
int GetNextNewActionID();
// Returns the active actions size. Default actions are marked deleted and
// still in `actions_`.
size_t GetActiveActionsSize();
// Returns true if there is only one user added action.
bool HasSingleUserAddedAction() const;
// Add a new action of type `action_type` from UI without input binding and
// with default position binding at the center.
void AddNewAction(ActionType action_type, const gfx::Point& target_pos);
void RemoveAction(Action* action);
// Create a new action with guidance from the reference action, and delete
// the reference action.
void ChangeActionType(Action* reference_action, ActionType action_type);
void RemoveActionNewState(Action* action);
void AddObserver(TouchInjectorObserver* observer);
void RemoveObserver(TouchInjectorObserver* observer);
// UMA stats.
void RecordMenuStateOnLaunch();
// ui::EventRewriter:
ui::EventDispatchDetails RewriteEvent(
const ui::Event& event,
const Continuation continuation) override;
aura::Window* window() const { return window_; }
const std::string& package_name() const { return package_name_; }
const gfx::RectF& content_bounds_f() const { return content_bounds_f_; }
const gfx::Rect content_bounds() const {
return gfx::ToEnclosingRect(content_bounds_f_);
}
const gfx::Transform* rotation_transform() {
return rotation_transform_.get();
}
const std::vector<std::unique_ptr<Action>>& actions() const {
return actions_;
}
bool is_mouse_locked() const { return is_mouse_locked_; }
bool touch_injector_enable() const { return touch_injector_enable_; }
void store_touch_injector_enable(bool enable) {
touch_injector_enable_ = enable;
}
bool input_mapping_visible() const { return input_mapping_visible_; }
void store_input_mapping_visible(bool enable) {
input_mapping_visible_ = enable;
}
void set_can_rewrite_event(bool can_rewrite_event) {
can_rewrite_event_ = can_rewrite_event;
}
void set_display_mode(DisplayMode mode) { display_mode_ = mode; }
void set_display_overlay_controller(DisplayOverlayController* controller) {
display_overlay_controller_ = controller;
}
bool enable_mouse_lock() { return enable_mouse_lock_; }
void set_enable_mouse_lock(bool enable) { enable_mouse_lock_ = true; }
private:
friend class ArcInputOverlayManagerTest;
friend class ButtonOptionsMenuTest;
friend class DisplayOverlayControllerTest;
friend class TouchInjectorTest;
struct TouchPointInfo {
// ID managed by input overlay.
int rewritten_touch_id;
// The latest root location of this given touch event.
gfx::PointF touch_root_location;
};
class KeyCommand;
// Clean up active touch events when there are pending touch events
// (`has_pending_touch_events_`== true) or to unlock the mouse if the mouse is
// locked. This is usually called when:
// 1. Exits from `kView` mode.
// 2. Mouse is not locked and it interrupts into the process.
void CleanupTouchEvents();
void SendExtraEvent(const ui::EventRewriter::Continuation continuation,
const ui::Event& event);
void DispatchTouchReleaseEventOnMouseUnLock();
void DispatchTouchReleaseEvent();
// Json format:
// "mouse_lock": {
// "key": "KeyA",
// "modifier": [""]
// }
void ParseMouseLock(const base::Value::Dict& dict);
void FlipMouseLockFlag();
// Check if the event located on menu entry. `press_required` tells whether or
// not a mouse press or touch press is required.
bool LocatedEventOnMenuEntry(const ui::Event& event,
const gfx::RectF& content_bounds,
bool press_required);
// Takes valid touch events and overrides their ids with an id managed by the
// TouchIdManager.
std::unique_ptr<ui::TouchEvent> RewriteOriginalTouch(
const ui::TouchEvent* touch_event);
// This method will generate a new touch event with a managed touch id.
std::unique_ptr<ui::TouchEvent> CreateTouchEvent(
const ui::TouchEvent* touch_event,
ui::PointerId original_id,
int managed_touch_id,
gfx::PointF root_location_f);
// Search action by its id.
Action* GetActionById(int id);
// Convert the customized data to AppDataProto.
std::unique_ptr<AppDataProto> ConvertToProto();
// Add the menu state to `proto`.
void AddMenuStateToProto(AppDataProto& proto);
// Load menu state from `proto`. The default state is on for the toggles.
void LoadMenuStateFromProto(AppDataProto& proto);
void AddSystemVersionToProto(AppDataProto& proto);
// Overwrite the default `action` from `proto`.
void OverwriteDefaultAction(const ActionProto& proto, Action* action);
// Add an action from proto if the default action has type changed or it is a
// user-added action.
void AddUserAddedActionFromProto(const ActionProto& proto);
// Find `action` in `actions_`, and then replace it by `new_action`. This is
// called when changing action type from UI or overwriting the default action
// with a different action type from proto.
void ReplaceActionInternal(Action* action,
std::unique_ptr<Action> new_action);
// For observers.
void NotifyActionAdded(Action& action);
void NotifyActionRemoved(Action& action);
void NotifyActionTypeChanged(Action* action, Action* new_action);
void NotifyActionInputBindingUpdated(const Action& action);
void NotifyContentBoundsSizeChanged();
void NotifyActionNewStateRemoved(Action& action);
// For test.
int GetRewrittenTouchIdForTesting(ui::PointerId original_id);
gfx::PointF GetRewrittenRootLocationForTesting(ui::PointerId original_id);
DisplayOverlayController* GetControllerForTesting();
// TouchInjector is created when targeted `window_` is created and is
// registered only when `window_` is focused. And TouchInjector doesn't own
// `window_` and it is destroyed when `window_` is destroyed.
raw_ptr<aura::Window, AcrossTasksDanglingUntriaged> window_;
std::string package_name_;
gfx::RectF content_bounds_f_;
base::WeakPtr<ui::EventRewriterContinuation> continuation_;
std::vector<std::unique_ptr<Action>> actions_;
base::ScopedObservation<ui::EventSource, ui::EventRewriter> observation_{
this};
std::unique_ptr<KeyCommand> mouse_lock_;
std::unique_ptr<gfx::Transform> rotation_transform_;
bool text_input_active_ = false;
// The mouse is unlocked by default.
bool is_mouse_locked_ = false;
DisplayMode display_mode_ = DisplayMode::kView;
raw_ptr<DisplayOverlayController> display_overlay_controller_ = nullptr;
// Linked to game controller toggle in the menu. Set it enabled by default.
// This is to save status if display overlay is destroyed during window
// operations.
bool touch_injector_enable_ = true;
// Linked to input mapping toggle in the menu. Set it enabled by default. This
// is to save status if display overlay is destroyed during window operations.
bool input_mapping_visible_ = true;
bool can_rewrite_event_ = true;
// It is true when the touch injector rewrites events to touch events and
// there might be pending touch events not released.
bool has_pending_touch_events_ = false;
// Used for UMA stats. Don't record the stats when users just switch the
// toggle back and forth and finish at the same state. Only record the state
// change once the menu is closed.
bool touch_injector_enable_uma_ = true;
bool input_mapping_visible_uma_ = true;
// Key is the original touch id. Value is a struct containing required info
// for this touch event.
base::flat_map<ui::PointerId, TouchPointInfo> rewritten_touch_infos_;
base::ReentrantObserverList<TouchInjectorObserver> observers_;
// Callback when saving proto file.
OnSaveProtoFileCallback save_file_callback_;
// TODO(cuicuiruan): It can be removed after the mouse lock is enabled for
// post MVP.
bool enable_mouse_lock_ = false;
// Use default position if it is null.
std::optional<gfx::Vector2dF> menu_entry_location_;
// Used to track whether the game has been played with Game Controls.
bool played_with_game_controls_ = false;
base::WeakPtrFactory<TouchInjector> weak_ptr_factory_{this};
};
} // namespace arc::input_overlay
#endif // CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_TOUCH_INJECTOR_H_
|