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