This commit is contained in:
redphx
2024-12-05 17:10:39 +07:00
parent c836e33f7b
commit 9199351af1
207 changed files with 9833 additions and 6953 deletions

232
src/modules/mkb/native-mkb-handler.ts Normal file → Executable file
View File

@@ -4,10 +4,14 @@ import { AppInterface, STATES } from "@/utils/global";
import { MkbHandler } from "./base-mkb-handler";
import { t } from "@/utils/translation";
import { BxEvent } from "@/utils/bx-event";
import { ButtonStyle, CE, createButton } from "@/utils/html";
import { PrefKey } from "@/enums/pref-keys";
import { getPref } from "@/utils/settings-storages/global-settings-storage";
import { BxLogger } from "@/utils/bx-logger";
import { MkbPopup } from "./mkb-popup";
import { KeyHelper } from "./key-helper";
import { StreamSettings } from "@/utils/stream-settings";
import { ShortcutAction } from "@/enums/shortcut-actions";
import { NativeMkbMode } from "@/enums/pref-values";
type NativeMouseData = {
X: number,
@@ -15,7 +19,7 @@ type NativeMouseData = {
Buttons: number,
WheelX: number,
WheelY: number,
Type? : 0, // 0: Relative, 1: Absolute
Type?: 0, // 0: Relative, 1: Absolute
}
type XcloudInputSink = {
@@ -23,30 +27,47 @@ type XcloudInputSink = {
}
export class NativeMkbHandler extends MkbHandler {
private static instance: NativeMkbHandler;
public static getInstance = () => NativeMkbHandler.instance ?? (NativeMkbHandler.instance = new NativeMkbHandler());
private static instance: NativeMkbHandler | null | undefined;
public static getInstance(): typeof NativeMkbHandler['instance'] {
if (typeof NativeMkbHandler.instance === 'undefined') {
if (NativeMkbHandler.isAllowed()) {
NativeMkbHandler.instance = new NativeMkbHandler();
} else {
NativeMkbHandler.instance = null;
}
}
return NativeMkbHandler.instance;
}
private readonly LOG_TAG = 'NativeMkbHandler';
#pointerClient: PointerClient | undefined;
#enabled: boolean = false;
static isAllowed = () => {
return STATES.browser.capabilities.emulatedNativeMkb && getPref<NativeMkbMode>(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.ON;
}
#mouseButtonsPressed = 0;
#mouseWheelX = 0;
#mouseWheelY = 0;
private pointerClient: PointerClient | undefined;
private enabled = false;
#mouseVerticalMultiply = 0;
#mouseHorizontalMultiply = 0;
private mouseButtonsPressed = 0;
private mouseWheelX = 0;
private mouseWheelY = 0;
#inputSink: XcloudInputSink | undefined;
private mouseVerticalMultiply = 0;
private mouseHorizontalMultiply = 0;
#$message?: HTMLElement;
private inputSink: XcloudInputSink | undefined;
private popup!: MkbPopup;
private constructor() {
super();
BxLogger.info(this.LOG_TAG, 'constructor()');
this.popup = MkbPopup.getInstance();
this.popup.attachMkbHandler(this);
}
#onKeyboardEvent(e: KeyboardEvent) {
private onKeyboardEvent(e: KeyboardEvent) {
if (e.type === 'keyup' && e.code === 'F8') {
e.preventDefault();
this.toggle();
@@ -54,110 +75,63 @@ export class NativeMkbHandler extends MkbHandler {
}
}
#onPointerLockRequested(e: Event) {
private onPointerLockRequested(e: Event) {
AppInterface.requestPointerCapture();
this.start();
}
#onPointerLockExited(e: Event) {
private onPointerLockExited(e: Event) {
AppInterface.releasePointerCapture();
this.stop();
}
#onPollingModeChanged = (e: Event) => {
if (!this.#$message) {
return;
}
const mode = (e as any).mode;
if (mode === 'none') {
this.#$message.classList.remove('bx-offscreen');
} else {
this.#$message.classList.add('bx-offscreen');
}
private onPollingModeChanged = (e: Event) => {
const move = window.BX_STREAM_SETTINGS.xCloudPollingMode !== 'none';
this.popup.moveOffscreen(move);
}
#onDialogShown = () => {
private onDialogShown = () => {
document.pointerLockElement && document.exitPointerLock();
}
#initMessage() {
if (!this.#$message) {
this.#$message = CE('div', {'class': 'bx-mkb-pointer-lock-msg'},
CE('div', {},
CE('p', {}, t('native-mkb')),
CE('p', {}, t('press-key-to-toggle-mkb', {key: 'F8'})),
),
CE('div', {'data-type': 'native'},
createButton({
style: ButtonStyle.PRIMARY | ButtonStyle.FULL_WIDTH | ButtonStyle.TALL,
label: t('activate'),
onClick: ((e: Event) => {
e.preventDefault();
e.stopPropagation();
this.toggle(true);
}).bind(this),
}),
createButton({
style: ButtonStyle.GHOST | ButtonStyle.FULL_WIDTH,
label: t('ignore'),
onClick: e => {
e.preventDefault();
e.stopPropagation();
this.#$message?.classList.add('bx-gone');
},
}),
),
);
}
if (!this.#$message.isConnected) {
document.documentElement.appendChild(this.#$message);
}
}
handleEvent(event: Event) {
switch (event.type) {
case 'keyup':
this.#onKeyboardEvent(event as KeyboardEvent);
this.onKeyboardEvent(event as KeyboardEvent);
break;
case BxEvent.XCLOUD_DIALOG_SHOWN:
this.#onDialogShown();
this.onDialogShown();
break;
case BxEvent.POINTER_LOCK_REQUESTED:
this.#onPointerLockRequested(event);
this.onPointerLockRequested(event);
break;
case BxEvent.POINTER_LOCK_EXITED:
this.#onPointerLockExited(event);
this.onPointerLockExited(event);
break;
case BxEvent.XCLOUD_POLLING_MODE_CHANGED:
this.#onPollingModeChanged(event);
this.onPollingModeChanged(event);
break;
}
}
init() {
this.#pointerClient = PointerClient.getInstance();
this.#inputSink = window.BX_EXPOSED.inputSink;
this.pointerClient = PointerClient.getInstance();
this.inputSink = window.BX_EXPOSED.inputSink;
// Stop keyboard input at startup
this.#updateInputConfigurationAsync(false);
this.updateInputConfigurationAsync(false);
try {
this.#pointerClient.start(STATES.pointerServerPort, this);
this.pointerClient.start(STATES.pointerServerPort, this);
} catch (e) {
Toast.show('Cannot enable Mouse & Keyboard feature');
}
this.#mouseVerticalMultiply = getPref(PrefKey.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY);
this.#mouseHorizontalMultiply = getPref(PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY);
this.mouseVerticalMultiply = getPref(PrefKey.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY);
this.mouseHorizontalMultiply = getPref(PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY);
window.addEventListener('keyup', this);
@@ -166,14 +140,13 @@ export class NativeMkbHandler extends MkbHandler {
window.addEventListener(BxEvent.POINTER_LOCK_EXITED, this);
window.addEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, this);
this.#initMessage();
if (AppInterface) {
Toast.show(t('press-key-to-toggle-mkb', {key: `<b>F8</b>`}), t('native-mkb'), {html: true});
this.#$message?.classList.add('bx-gone');
} else {
this.#$message?.classList.remove('bx-gone');
const shortcutKey = StreamSettings.findKeyboardShortcut(ShortcutAction.MKB_TOGGLE);
if (shortcutKey) {
const msg = t('press-key-to-toggle-mkb', { key: `<b>${KeyHelper.codeToKeyName(shortcutKey)}</b>` });
Toast.show(msg, t('native-mkb'), { html: true });
}
this.waitForMouseData(false);
}
toggle(force?: boolean) {
@@ -181,7 +154,7 @@ export class NativeMkbHandler extends MkbHandler {
if (typeof force !== 'undefined') {
setEnable = force;
} else {
setEnable = !this.#enabled;
setEnable = !this.enabled;
}
if (setEnable) {
@@ -191,7 +164,7 @@ export class NativeMkbHandler extends MkbHandler {
}
}
#updateInputConfigurationAsync(enabled: boolean) {
private updateInputConfigurationAsync(enabled: boolean) {
window.BX_EXPOSED.streamSession.updateInputConfigurationAsync({
enableKeyboardInput: enabled,
enableMouseInput: enabled,
@@ -201,27 +174,27 @@ export class NativeMkbHandler extends MkbHandler {
}
start() {
this.#resetMouseInput();
this.#enabled = true;
this.resetMouseInput();
this.enabled = true;
this.#updateInputConfigurationAsync(true);
this.updateInputConfigurationAsync(true);
window.BX_EXPOSED.stopTakRendering = true;
this.#$message?.classList.add('bx-gone');
this.waitForMouseData(false);
Toast.show(t('native-mkb'), t('enabled'), {instant: true});
}
stop() {
this.#resetMouseInput();
this.#enabled = false;
this.#updateInputConfigurationAsync(false);
this.resetMouseInput();
this.enabled = false;
this.updateInputConfigurationAsync(false);
this.#$message?.classList.remove('bx-gone');
this.waitForMouseData(true);
}
destroy(): void {
this.#pointerClient?.stop();
this.pointerClient?.stop();
window.removeEventListener('keyup', this);
window.removeEventListener(BxEvent.XCLOUD_DIALOG_SHOWN, this);
@@ -229,16 +202,16 @@ export class NativeMkbHandler extends MkbHandler {
window.removeEventListener(BxEvent.POINTER_LOCK_EXITED, this);
window.removeEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, this);
this.#$message?.classList.add('bx-gone');
this.waitForMouseData(false);
}
handleMouseMove(data: MkbMouseMove): void {
this.#sendMouseInput({
this.sendMouseInput({
X: data.movementX,
Y: data.movementY,
Buttons: this.#mouseButtonsPressed,
WheelX: this.#mouseWheelX,
WheelY: this.#mouseWheelY,
Buttons: this.mouseButtonsPressed,
WheelX: this.mouseWheelX,
WheelY: this.mouseWheelY,
});
}
@@ -246,71 +219,72 @@ export class NativeMkbHandler extends MkbHandler {
const { pointerButton, pressed } = data;
if (pressed) {
this.#mouseButtonsPressed |= pointerButton!;
this.mouseButtonsPressed |= pointerButton!;
} else {
this.#mouseButtonsPressed ^= pointerButton!;
this.mouseButtonsPressed ^= pointerButton!;
}
this.#mouseButtonsPressed = Math.max(0, this.#mouseButtonsPressed);
this.mouseButtonsPressed = Math.max(0, this.mouseButtonsPressed);
this.#sendMouseInput({
this.sendMouseInput({
X: 0,
Y: 0,
Buttons: this.#mouseButtonsPressed,
WheelX: this.#mouseWheelX,
WheelY: this.#mouseWheelY,
Buttons: this.mouseButtonsPressed,
WheelX: this.mouseWheelX,
WheelY: this.mouseWheelY,
});
}
handleMouseWheel(data: MkbMouseWheel): boolean {
const { vertical, horizontal } = data;
this.#mouseWheelX = horizontal;
if (this.#mouseHorizontalMultiply && this.#mouseHorizontalMultiply !== 1) {
this.#mouseWheelX *= this.#mouseHorizontalMultiply;
this.mouseWheelX = horizontal;
if (this.mouseHorizontalMultiply && this.mouseHorizontalMultiply !== 1) {
this.mouseWheelX *= this.mouseHorizontalMultiply;
}
this.#mouseWheelY = vertical;
if (this.#mouseVerticalMultiply && this.#mouseVerticalMultiply !== 1) {
this.#mouseWheelY *= this.#mouseVerticalMultiply;
this.mouseWheelY = vertical;
if (this.mouseVerticalMultiply && this.mouseVerticalMultiply !== 1) {
this.mouseWheelY *= this.mouseVerticalMultiply;
}
this.#sendMouseInput({
this.sendMouseInput({
X: 0,
Y: 0,
Buttons: this.#mouseButtonsPressed,
WheelX: this.#mouseWheelX,
WheelY: this.#mouseWheelY,
Buttons: this.mouseButtonsPressed,
WheelX: this.mouseWheelX,
WheelY: this.mouseWheelY,
});
return true;
}
setVerticalScrollMultiplier(vertical: number) {
this.#mouseVerticalMultiply = vertical;
this.mouseVerticalMultiply = vertical;
}
setHorizontalScrollMultiplier(horizontal: number) {
this.#mouseHorizontalMultiply = horizontal;
this.mouseHorizontalMultiply = horizontal;
}
waitForMouseData(enabled: boolean): void {
waitForMouseData(showPopup: boolean) {
this.popup.toggleVisibility(showPopup);
}
isEnabled(): boolean {
return this.#enabled;
return this.enabled;
}
#sendMouseInput(data: NativeMouseData) {
private sendMouseInput(data: NativeMouseData) {
data.Type = 0; // Relative
this.#inputSink?.onMouseInput(data);
this.inputSink?.onMouseInput(data);
}
#resetMouseInput() {
this.#mouseButtonsPressed = 0;
this.#mouseWheelX = 0;
this.#mouseWheelY = 0;
private resetMouseInput() {
this.mouseButtonsPressed = 0;
this.mouseWheelX = 0;
this.mouseWheelY = 0;
this.#sendMouseInput({
this.sendMouseInput({
X: 0,
Y: 0,
Buttons: 0,