1/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17'use strict';
18
19function trackPointerEvents(touchInputElement, dc, scaleCoordinates) {
20  let activePointers = new Set();
21
22  function onPointerDown(e) {
23    // Can't prevent event default behavior to allow the element gain focus
24    // when touched and start capturing keyboard input in the parent.
25    activePointers.add(e.pointerId);
26    sendEventUpdate(dc, e.target, [{x: e.offsetX, y: e.offsetY, id: e.pointerId}], scaleCoordinates, true);
27  }
28
29  function onPointerUp(e) {
30    // Can't prevent event default behavior to allow the element gain focus
31    // when touched and start capturing keyboard input in the parent.
32    const wasDown = activePointers.delete(e.pointerId);
33    if (!wasDown) {
34      return;
35    }
36    sendEventUpdate(dc, e.target, [{x: e.offsetX, y: e.offsetY, id: e.pointerId}], scaleCoordinates, false);
37  }
38
39  function onPointerMove(e) {
40    // Can't prevent event default behavior to allow the element gain focus
41    // when touched and start capturing keyboard input in the parent.
42    if (!activePointers.has(e.pointerId)) {
43      // This is just a mouse move, not a drag
44      return;
45    }
46    sendEventUpdate(dc, e.target, [{x: e.offsetX, y: e.offsetY, id: e.pointerId}], scaleCoordinates, true);
47  }
48
49  touchInputElement.addEventListener('pointerdown', onPointerDown);
50  touchInputElement.addEventListener('pointermove', onPointerMove);
51  touchInputElement.addEventListener('pointerup', onPointerUp);
52  touchInputElement.addEventListener('pointerleave', onPointerUp);
53  touchInputElement.addEventListener('pointercancel', onPointerUp);
54}
55
56function scaleDisplayCoordinates(deviceDisplayElement, x, y) {
57  // Before the first video frame arrives there is no way to know width and
58  // height of the device's screen, so turn every click into a click at 0x0.
59  // A click at that position is not more dangerous than anywhere else since
60  // the user is clicking blind anyways.
61  const actualWidth = deviceDisplayElement.videoWidth ? deviceDisplayElement.videoWidth : 1;
62  const elementWidth = deviceDisplayElement.offsetWidth ? deviceDisplayElement.offsetWidth : 1;
63  const scaling = actualWidth / elementWidth;
64  return [Math.trunc(x * scaling), Math.trunc(y * scaling)];
65}
66
67function makeScaleTouchpadCoordinates(touchpad) {
68  return (touchpadElement, x, y)  => {
69    const elementWidth = touchpadElement.offsetWidth ? touchpadElement.offsetWidth : 1;
70    const scaling = touchpad.x_res / elementWidth;
71    return [Math.trunc(x * scaling), Math.trunc(y * scaling)];
72  };
73}
74
75function sendEventUpdate(dc, touchInputElement, evs /*[{x, y, id}]*/, scaleCoordinates, down) {
76  if (evs.length == 0) {
77    return;
78  }
79
80  const device_label = touchInputElement.id;
81  let xArr = [];
82  let yArr = [];
83  let idArr = [];
84
85  for (const e of evs) {
86      const [x_scaled, y_scaled] = scaleCoordinates(touchInputElement, e.x, e.y);
87      xArr.push(x_scaled);
88      yArr.push(y_scaled);
89      idArr.push(e.id);
90  }
91
92  // NOTE: Rotation is handled automatically because the CSS rotation through
93  // transforms also rotates the coordinates of events on the object.
94  dc.sendMultiTouch(
95      {idArr, xArr, yArr, down: down, device_label});
96}
97
98