Skip to content
This repository has been archived by the owner on Mar 1, 2024. It is now read-only.

Commit

Permalink
Enable/disable user input devices (#136)
Browse files Browse the repository at this point in the history
* move registerLockedMouseEvents and registerHoveringMouseEvents to MouseController

* add a utility that keeps track of registered event handlers for easy unregistering

* fix stream handler name MoveMouse -> MouseMove

* support unregistering gamepad events

* support unregistering keyboard events

* support unregistering mouse events

* support unregistering touch events

* unregister input event handlers before registering new ones

* configurable keyboard/mouse/touch/gamepad support

* start/stop game pad polling on config enable/disable

* UI: add user input config to Settings menu

* added config flag EnableXRControllerInput for enabling/disabling XR controller messages

* added the new config flags to documentation

* smaller version of the settings panel image to better match the old image resolution

* Simplify input flag naming. Separate input toggles into new section in the settings panel
  • Loading branch information
hmuurine authored Mar 6, 2023
1 parent 9de61bf commit 95f2bef
Show file tree
Hide file tree
Showing 21 changed files with 563 additions and 165 deletions.
Binary file modified Frontend/Docs/Resources/Images/settings-panel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions Frontend/Docs/Settings Panel.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ This page will be updated with new features and commands as they become availabl
| **Control scheme** | If the scheme is `locked mouse` the browser will use `pointerlock` to capture your mouse, whereas if the scheme is `hovering mouse` you will retain your OS/browser cursor. |
| **Color scheme** | Allows you to switch between light mode and dark mode. |

### Input
| **Setting** | **Description** |
| --- | --- |
| **Keyboard input** | If enabled, captures and sends keyboard events to the Unreal Engine application. |
| **Mouse input** | If enabled, captures and sends mouse events to the Unreal Engine application. |
| **Touch input** | If enabled, captures and sends touch events to the Unreal Engine application. |
| **Gamepad input** | If enabled, captures and sends gamepad events to the Unreal Engine application. |
| **XR controller input** | If enabled, captures and sends XR controller events to the Unreal Engine application. |

### Encoder
| **Setting** | **Description** |
| --- | --- |
Expand Down
4 changes: 2 additions & 2 deletions Frontend/library/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions Frontend/library/src/Config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ export class Flags {
static StartVideoMuted = 'StartVideoMuted' as const;
static SuppressBrowserKeys = 'SuppressBrowserKeys' as const;
static UseMic = 'UseMic' as const;
static KeyboardInput = 'KeyboardInput' as const;
static MouseInput = 'MouseInput' as const;
static TouchInput = 'TouchInput' as const;
static GamepadInput = 'GamepadInput' as const;
static XRControllerInput = 'XRControllerInput' as const;
}

export type FlagsKeys = Exclude<keyof typeof Flags, 'prototype'>;
Expand Down Expand Up @@ -398,6 +403,61 @@ export class Config {
)
);

this.flags.set(
Flags.KeyboardInput,
new SettingFlag(
Flags.KeyboardInput,
'Keyboard input',
'If enabled, send keyboard events to streamer',
true,
useUrlParams
)
);

this.flags.set(
Flags.MouseInput,
new SettingFlag(
Flags.MouseInput,
'Mouse input',
'If enabled, send mouse events to streamer',
true,
useUrlParams
)
);

this.flags.set(
Flags.TouchInput,
new SettingFlag(
Flags.TouchInput,
'Touch input',
'If enabled, send touch events to streamer',
true,
useUrlParams
)
);

this.flags.set(
Flags.GamepadInput,
new SettingFlag(
Flags.GamepadInput,
'Gamepad input',
'If enabled, send gamepad events to streamer',
true,
useUrlParams
)
);

this.flags.set(
Flags.XRControllerInput,
new SettingFlag(
Flags.XRControllerInput,
'XR controller input',
'If enabled, send XR controller events to streamer',
true,
useUrlParams
)
);

/**
* Numeric parameters
*/
Expand Down
39 changes: 31 additions & 8 deletions Frontend/library/src/Inputs/FakeTouchController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { StreamMessageController } from '../UeInstanceMessage/StreamMessageContr
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
Expand All @@ -18,6 +19,9 @@ export class FakeTouchController implements ITouchController {
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
Expand All @@ -31,9 +35,28 @@ export class FakeTouchController implements ITouchController {
this.toStreamerMessagesProvider = toStreamerMessagesProvider;
this.videoElementProvider = videoElementProvider;
this.coordinateConverter = coordinateConverter;
document.ontouchstart = (ev: TouchEvent) => this.onTouchStart(ev);
document.ontouchend = (ev: TouchEvent) => this.onTouchEnd(ev);
document.ontouchmove = (ev: TouchEvent) => this.onTouchMove(ev);
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();
}

/**
Expand Down Expand Up @@ -62,8 +85,8 @@ export class FakeTouchController implements ITouchController {

const videoElementParent =
this.videoElementProvider.getVideoParentElement() as HTMLDivElement;
const mouseEvent = new MouseEvent(touch.type, first_touch);
videoElementParent.onmouseenter(mouseEvent);
const mouseEvent = new MouseEvent('mouseenter', first_touch);
videoElementParent.dispatchEvent(mouseEvent);

const coord = this.coordinateConverter.normalizeAndQuantizeUnsigned(
this.fakeTouchFinger.x,
Expand Down Expand Up @@ -107,8 +130,8 @@ export class FakeTouchController implements ITouchController {
coord.y
]);

const mouseEvent = new MouseEvent(touchEvent.type, touch);
videoElementParent.onmouseleave(mouseEvent);
const mouseEvent = new MouseEvent('mouseleave', touch);
videoElementParent.dispatchEvent(mouseEvent);
this.fakeTouchFinger = null;
break;
}
Expand Down Expand Up @@ -140,7 +163,7 @@ export class FakeTouchController implements ITouchController {
x - this.fakeTouchFinger.x,
y - this.fakeTouchFinger.y
);
toStreamerHandlers.get('MoveMouse')([
toStreamerHandlers.get('MouseMove')([
coord.x,
coord.y,
delta.x,
Expand Down
56 changes: 42 additions & 14 deletions Frontend/library/src/Inputs/GamepadController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { Logger } from '../Logger/Logger';
import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController';
import { EventListenerTracker } from '../Util/EventListenerTracker';
import { Controller } from './GamepadTypes';

/**
Expand All @@ -12,35 +13,60 @@ export class GamePadController {
requestAnimationFrame: (callback: FrameRequestCallback) => number;
toStreamerMessagesProvider: StreamMessageController;

// Utility for keeping track of event handlers and unregistering them
private gamePadEventListenerTracker = new EventListenerTracker();

/**
* @param toStreamerMessagesProvider - Stream message instance
*/
constructor(toStreamerMessagesProvider: StreamMessageController) {
this.toStreamerMessagesProvider = toStreamerMessagesProvider;

this.requestAnimationFrame =
this.requestAnimationFrame = (
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.requestAnimationFrame;
window.requestAnimationFrame
).bind(window);
const browserWindow = window as Window;
if ('GamepadEvent' in browserWindow) {
window.addEventListener('gamepadconnected', (ev: GamepadEvent) =>
this.gamePadConnectHandler(ev)
const onGamePadConnected = (ev: GamepadEvent) =>
this.gamePadConnectHandler(ev);
const onGamePadDisconnected = (ev: GamepadEvent) =>
this.gamePadDisconnectHandler(ev);
window.addEventListener('gamepadconnected', onGamePadConnected);
window.addEventListener('gamepaddisconnected', onGamePadDisconnected);
this.gamePadEventListenerTracker.addUnregisterCallback(
() => window.removeEventListener('gamepadconnected', onGamePadConnected)
);
window.addEventListener('gamepaddisconnected', (ev: GamepadEvent) =>
this.gamePadDisconnectHandler(ev)
this.gamePadEventListenerTracker.addUnregisterCallback(
() => window.removeEventListener('gamepaddisconnected', onGamePadDisconnected)
);
} else if ('WebKitGamepadEvent' in browserWindow) {
window.addEventListener(
'webkitgamepadconnected',
(ev: GamepadEvent) => this.gamePadConnectHandler(ev)
const onWebkitGamePadConnected = (ev: GamepadEvent) => this.gamePadConnectHandler(ev);
const onWebkitGamePadDisconnected = (ev: GamepadEvent) => this.gamePadDisconnectHandler(ev);
window.addEventListener('webkitgamepadconnected', onWebkitGamePadConnected);
window.addEventListener('webkitgamepaddisconnected', onWebkitGamePadDisconnected);
this.gamePadEventListenerTracker.addUnregisterCallback(
() => window.removeEventListener('webkitgamepadconnected', onWebkitGamePadConnected)
);
window.addEventListener(
'webkitgamepaddisconnected',
(ev: GamepadEvent) => this.gamePadDisconnectHandler(ev)
this.gamePadEventListenerTracker.addUnregisterCallback(
() => window.removeEventListener('webkitgamepaddisconnected', onWebkitGamePadDisconnected)
);
}
this.controllers = [];
for (const gamepad of navigator.getGamepads()) {
if (gamepad) {
this.gamePadConnectHandler(new GamepadEvent('gamepadconnected', { gamepad }));
}
}
}

/**
* Unregisters all event handlers
*/
unregisterGamePadEvents() {
this.gamePadEventListenerTracker.unregisterAll();
this.controllers = [];
}

/**
Expand All @@ -64,7 +90,7 @@ export class GamePadController {
'gamepad: ' + gamepad.id + ' connected',
6
);
window.requestAnimationFrame(() => this.updateStatus());
this.requestAnimationFrame(() => this.updateStatus());
}

/**
Expand Down Expand Up @@ -185,7 +211,9 @@ export class GamePadController {
}
this.controllers[controllerIndex].prevState = currentState;
}
this.requestAnimationFrame(() => this.updateStatus());
if (this.controllers.length > 0) {
this.requestAnimationFrame(() => this.updateStatus());
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions Frontend/library/src/Inputs/HoveringMouseEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ export class HoveringMouseEvents implements IMouseEvents {
this.mouseController = mouseController;
}

/**
* Unregister event handlers
*/
unregisterMouseEvents(): void {
// empty for HoveringMouseEvents implementation
}

/**
* Handle the mouse move event, sends the mouse data to the UE Instance
* @param mouseEvent - Mouse Event
Expand Down
5 changes: 5 additions & 0 deletions Frontend/library/src/Inputs/IMouseEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,9 @@ export interface IMouseEvents {
* @param mouseEvent - mouse event
*/
handleContextMenu?(mouseEvent: MouseEvent): void;

/**
* Unregisters any registered mouse event handlers
*/
unregisterMouseEvents(): void;
}
5 changes: 5 additions & 0 deletions Frontend/library/src/Inputs/ITouchController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ export interface ITouchController {
* @param touchEvent - Touch Event Data
*/
onTouchMove(touchEvent: TouchEvent): void;

/**
* Unregisters all touch event handlers
*/
unregisterTouchEvents(): void;
}
Loading

0 comments on commit 95f2bef

Please sign in to comment.