@epicgames-ps/lib-pixelstreamingfrontend-ue5.4
Version:
Frontend library for Unreal Engine 5.4 Pixel Streaming
200 lines (183 loc) • 7.63 kB
text/typescript
// Copyright Epic Games, Inc. All Rights Reserved.
import { CoordinateConverter } from '../Util/CoordinateConverter';
import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController';
import { VideoPlayer } from '../VideoPlayer/VideoPlayer';
import { ITouchController } from './ITouchController';
import { MouseButton } from './MouseButtons';
import { EventListenerTracker } from '../Util/EventListenerTracker';
/**
* Allows for the usage of fake touch events and implements ITouchController
* @param dataChannelController - The controller for the Data channel
* @param videoElementParent - The video player DOM element
*/
export class FakeTouchController implements ITouchController {
fakeTouchFinger: FakeTouchFinger;
toStreamerMessagesProvider: StreamMessageController;
videoElementProvider: VideoPlayer;
coordinateConverter: CoordinateConverter;
videoElementParentClientRect: DOMRect;
// Utility for keeping track of event handlers and unregistering them
private touchEventListenerTracker = new EventListenerTracker();
/**
* @param toStreamerMessagesProvider - Stream message instance
* @param videoElementProvider - Video element instance
* @param coordinateConverter - A coordinate converter instance
*/
constructor(
toStreamerMessagesProvider: StreamMessageController,
videoElementProvider: VideoPlayer,
coordinateConverter: CoordinateConverter
) {
this.toStreamerMessagesProvider = toStreamerMessagesProvider;
this.videoElementProvider = videoElementProvider;
this.coordinateConverter = coordinateConverter;
const ontouchstart = (ev: TouchEvent) => this.onTouchStart(ev);
const ontouchend = (ev: TouchEvent) => this.onTouchEnd(ev);
const ontouchmove = (ev: TouchEvent) => this.onTouchMove(ev);
document.addEventListener('touchstart', ontouchstart, { passive: false });
document.addEventListener('touchend', ontouchend, { passive: false });
document.addEventListener('touchmove', ontouchmove, { passive: false });
this.touchEventListenerTracker.addUnregisterCallback(
() => document.removeEventListener('touchstart', ontouchstart)
);
this.touchEventListenerTracker.addUnregisterCallback(
() => document.removeEventListener('touchend', ontouchend)
);
this.touchEventListenerTracker.addUnregisterCallback(
() => document.removeEventListener('touchmove', ontouchmove)
);
}
/**
* Unregister all touch events
*/
unregisterTouchEvents() {
this.touchEventListenerTracker.unregisterAll();
}
/**
* Sets the video Element Parent Client Rect numbers for this class
* @param videoElementParentClientRect - a html ElementParentClientRect object
*/
setVideoElementParentClientRect(videoElementParentClientRect: DOMRect) {
this.videoElementParentClientRect = videoElementParentClientRect;
}
/**
* When a touch event begins
* @param touch - the activating touch event
*/
onTouchStart(touch: TouchEvent): void {
if (!this.videoElementProvider.isVideoReady() || touch.target !== this.videoElementProvider.getVideoElement()) {
return;
}
if (this.fakeTouchFinger == null) {
const first_touch = touch.changedTouches[0];
this.fakeTouchFinger = new FakeTouchFinger(
first_touch.identifier,
first_touch.clientX - this.videoElementParentClientRect.left,
first_touch.clientY - this.videoElementParentClientRect.top
);
const videoElementParent =
this.videoElementProvider.getVideoParentElement() as HTMLDivElement;
const mouseEvent = new MouseEvent('mouseenter', first_touch);
videoElementParent.dispatchEvent(mouseEvent);
const coord = this.coordinateConverter.normalizeAndQuantizeUnsigned(
this.fakeTouchFinger.x,
this.fakeTouchFinger.y
);
const toStreamerHandlers =
this.toStreamerMessagesProvider.toStreamerHandlers;
toStreamerHandlers.get('MouseDown')([
MouseButton.mainButton,
coord.x,
coord.y
]);
}
touch.preventDefault();
}
/**
* When a touch event ends
* @param touchEvent - the activating touch event
*/
onTouchEnd(touchEvent: TouchEvent): void {
if (!this.videoElementProvider.isVideoReady() || this.fakeTouchFinger == null) {
return;
}
const videoElementParent =
this.videoElementProvider.getVideoParentElement();
const toStreamerHandlers =
this.toStreamerMessagesProvider.toStreamerHandlers;
for (let t = 0; t < touchEvent.changedTouches.length; t++) {
const touch = touchEvent.changedTouches[t];
if (touch.identifier === this.fakeTouchFinger.id) {
const x =
touch.clientX - this.videoElementParentClientRect.left;
const y = touch.clientY - this.videoElementParentClientRect.top;
const coord =
this.coordinateConverter.normalizeAndQuantizeUnsigned(x, y);
toStreamerHandlers.get('MouseUp')([
MouseButton.mainButton,
coord.x,
coord.y
]);
const mouseEvent = new MouseEvent('mouseleave', touch);
videoElementParent.dispatchEvent(mouseEvent);
this.fakeTouchFinger = null;
break;
}
}
touchEvent.preventDefault();
}
/**
* On a Move touch event
* @param touchEvent - the activating touch event
*/
onTouchMove(touchEvent: TouchEvent): void {
if (!this.videoElementProvider.isVideoReady() || this.fakeTouchFinger == null) {
return;
}
const toStreamerHandlers =
this.toStreamerMessagesProvider.toStreamerHandlers;
for (let t = 0; t < touchEvent.touches.length; t++) {
const touch = touchEvent.touches[t];
if (touch.identifier === this.fakeTouchFinger.id) {
const x =
touch.clientX - this.videoElementParentClientRect.left;
const y = touch.clientY - this.videoElementParentClientRect.top;
const coord =
this.coordinateConverter.normalizeAndQuantizeUnsigned(x, y);
const delta =
this.coordinateConverter.normalizeAndQuantizeSigned(
x - this.fakeTouchFinger.x,
y - this.fakeTouchFinger.y
);
toStreamerHandlers.get('MouseMove')([
coord.x,
coord.y,
delta.x,
delta.y
]);
this.fakeTouchFinger.x = x;
this.fakeTouchFinger.y = y;
break;
}
}
touchEvent.preventDefault();
}
}
/**
* The interface for finger position mapping
*/
export class FakeTouchFinger {
id: number;
x: number;
y: number;
/**
* @param id - the button id
* @param x - the x axis value
* @param y - the y axis value
*/
constructor(id: number, x: number, y: number) {
this.id = id;
this.x = x;
this.y = y;
}
}