File: touch_injector.h

package info (click to toggle)
chromium 138.0.7204.183-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,080,960 kB
  • sloc: cpp: 34,937,079; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,954; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,811; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (327 lines) | stat: -rw-r--r-- 12,201 bytes parent folder | download | duplicates (5)
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_