mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-06 07:37:19 +02:00
Allow controlling settings using gamepad
This commit is contained in:
parent
2a0af5d0ab
commit
64568532cb
@ -452,6 +452,17 @@ BxEvent.dispatch(window, BxEvent.XCLOUD_POLLING_MODE_CHANGED, {mode: e});
|
||||
return str;
|
||||
},
|
||||
|
||||
patchGamepadPolling(str: string) {
|
||||
let index = str.indexOf('.shouldHandleGamepadInput)())return void');
|
||||
if (index === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
index = str.indexOf('{', index - 20) + 1;
|
||||
str = str.substring(0, index) + 'if (window.BX_EXPOSED.disableGamepadPolling) return;' + str.substring(index);
|
||||
return str;
|
||||
},
|
||||
|
||||
patchXcloudTitleInfo(str: string) {
|
||||
const text = 'async cloudConnect';
|
||||
let index = str.indexOf(text);
|
||||
@ -803,6 +814,7 @@ let PATCH_ORDERS: PatchArray = [
|
||||
'disableStreamGate',
|
||||
'overrideSettings',
|
||||
'broadcastPollingMode',
|
||||
getPref(PrefKey.UI_CONTROLLER_FRIENDLY) && 'patchGamepadPolling',
|
||||
|
||||
'exposeStreamSession',
|
||||
'exposeDialogRoutes',
|
||||
|
@ -1,3 +1,8 @@
|
||||
if (window.BX_EXPOSED.disableGamepadPolling) {
|
||||
this.inputConfiguration.useIntervalWorkerThreadForInput && this.intervalWorker ? this.intervalWorker.scheduleTimer(4) : this.pollGamepadssetTimeoutTimerID = setTimeout(this.pollGamepads, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentGamepad = ${gamepadVar};
|
||||
|
||||
// Share button on XS controller
|
||||
|
@ -13,6 +13,7 @@ import { VibrationManager } from "../vibration-manager";
|
||||
import { StreamStats } from "./stream-stats";
|
||||
import { BxSelectElement } from "@/web-components/bx-select";
|
||||
import { onChangeVideoPlayerType, updateVideoPlayer } from "./stream-settings-utils";
|
||||
import { GamepadKey } from "@/enums/mkb";
|
||||
|
||||
enum FocusDirection {
|
||||
UP,
|
||||
@ -38,6 +39,21 @@ export class StreamSettings {
|
||||
return StreamSettings.instance;
|
||||
}
|
||||
|
||||
private static readonly GAMEPAD_POLLING_INTERVAL = 50;
|
||||
private static readonly GAMEPAD_KEYS = [
|
||||
GamepadKey.UP,
|
||||
GamepadKey.DOWN,
|
||||
GamepadKey.LEFT,
|
||||
GamepadKey.RIGHT,
|
||||
GamepadKey.A,
|
||||
GamepadKey.B,
|
||||
GamepadKey.LB,
|
||||
GamepadKey.RB,
|
||||
];
|
||||
|
||||
private gamepadPollingIntervalId: number | null = null;
|
||||
private gamepadLastButtons: Array<GamepadKey | null> = [];
|
||||
|
||||
private $container: HTMLElement | undefined;
|
||||
private $tabs: HTMLElement | undefined;
|
||||
private $settings: HTMLElement | undefined;
|
||||
@ -282,6 +298,12 @@ export class StreamSettings {
|
||||
|
||||
// Add event listeners
|
||||
$container.addEventListener('keydown', this);
|
||||
|
||||
// Start gamepad polling
|
||||
this.#startGamepadPolling();
|
||||
|
||||
// Disable xCloud's navigation polling
|
||||
(window as any).BX_EXPOSED.disableGamepadPolling = true;
|
||||
}
|
||||
|
||||
BxEvent.dispatch(window, BxEvent.XCLOUD_DIALOG_SHOWN);
|
||||
@ -298,9 +320,94 @@ export class StreamSettings {
|
||||
// Remove event listeners
|
||||
this.$container!.removeEventListener('keydown', this);
|
||||
|
||||
// Stop gamepad polling();
|
||||
this.#stopGamepadPolling();
|
||||
|
||||
// Enable xCloud's navigation polling
|
||||
(window as any).BX_EXPOSED.disableGamepadPolling = false;
|
||||
|
||||
BxEvent.dispatch(window, BxEvent.XCLOUD_DIALOG_DISMISSED);
|
||||
}
|
||||
|
||||
#pollGamepad() {
|
||||
const gamepads = window.navigator.getGamepads();
|
||||
|
||||
let direction: FocusDirection | null = null;
|
||||
for (const gamepad of gamepads) {
|
||||
if (!gamepad || !gamepad.connected) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const buttons = gamepad.buttons;
|
||||
let lastButton = this.gamepadLastButtons[gamepad.index];
|
||||
let pressedButton: GamepadKey | undefined = undefined;
|
||||
|
||||
for (const key of StreamSettings.GAMEPAD_KEYS) {
|
||||
if (typeof lastButton === 'number') {
|
||||
// Key pressed
|
||||
if (lastButton === key && !buttons[key].pressed) {
|
||||
pressedButton = key;
|
||||
break;
|
||||
}
|
||||
} else if (buttons[key].pressed) {
|
||||
this.gamepadLastButtons[gamepad.index] = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof pressedButton !== 'undefined') {
|
||||
this.gamepadLastButtons[gamepad.index] = null;
|
||||
|
||||
if (pressedButton === GamepadKey.A) {
|
||||
document.activeElement && document.activeElement.dispatchEvent(new MouseEvent('click'));
|
||||
} else if (pressedButton === GamepadKey.B) {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
if (pressedButton === GamepadKey.UP) {
|
||||
direction = FocusDirection.UP;
|
||||
} else if (pressedButton === GamepadKey.DOWN) {
|
||||
direction = FocusDirection.DOWN;
|
||||
} else if (pressedButton === GamepadKey.LEFT) {
|
||||
direction = FocusDirection.LEFT;
|
||||
} else if (pressedButton === GamepadKey.RIGHT) {
|
||||
direction = FocusDirection.RIGHT;
|
||||
}
|
||||
|
||||
if (direction !== null) {
|
||||
let handled = false;
|
||||
if (document.activeElement instanceof HTMLInputElement && document.activeElement.type === 'range') {
|
||||
const $range = document.activeElement;
|
||||
if (direction === FocusDirection.LEFT || direction === FocusDirection.RIGHT) {
|
||||
$range.value = (parseInt($range.value) + parseInt($range.step) * (direction === FocusDirection.LEFT ? -1 : 1)).toString();
|
||||
$range.dispatchEvent(new InputEvent('input'));
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
this.#focusDirection(direction);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#startGamepadPolling() {
|
||||
this.#stopGamepadPolling();
|
||||
|
||||
this.gamepadPollingIntervalId = window.setInterval(this.#pollGamepad.bind(this), StreamSettings.GAMEPAD_POLLING_INTERVAL);
|
||||
}
|
||||
|
||||
#stopGamepadPolling() {
|
||||
this.gamepadLastButtons = [];
|
||||
|
||||
this.gamepadPollingIntervalId && window.clearInterval(this.gamepadPollingIntervalId);
|
||||
this.gamepadPollingIntervalId = null;
|
||||
}
|
||||
|
||||
#handleTabsNavigation($focusing: HTMLElement, direction: FocusDirection) {
|
||||
if (direction === FocusDirection.UP || direction === FocusDirection.DOWN) {
|
||||
let $sibling = $focusing;
|
||||
@ -418,7 +525,7 @@ export class StreamSettings {
|
||||
case 'keydown':
|
||||
const $target = event.target as HTMLElement;
|
||||
const keyboardEvent = event as KeyboardEvent;
|
||||
const keyCode = keyboardEvent.code;
|
||||
const keyCode = keyboardEvent.code || keyboardEvent.key;
|
||||
|
||||
if (keyCode === 'ArrowUp' || keyCode === 'ArrowDown') {
|
||||
event.preventDefault();
|
||||
|
@ -115,4 +115,6 @@ export const BxExposed = {
|
||||
hasCompletedOnboarding: true,
|
||||
},
|
||||
},
|
||||
|
||||
disableGamepadPolling: false,
|
||||
};
|
||||
|
@ -714,6 +714,7 @@ export class Preferences {
|
||||
default: 100,
|
||||
min: 0,
|
||||
max: 600,
|
||||
steps: 20,
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 100,
|
||||
@ -772,6 +773,7 @@ export class Preferences {
|
||||
default: 80,
|
||||
min: 50,
|
||||
max: 100,
|
||||
steps: 10,
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 10,
|
||||
|
Loading…
x
Reference in New Issue
Block a user