mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-07 08:07:18 +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;
|
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) {
|
patchXcloudTitleInfo(str: string) {
|
||||||
const text = 'async cloudConnect';
|
const text = 'async cloudConnect';
|
||||||
let index = str.indexOf(text);
|
let index = str.indexOf(text);
|
||||||
@ -803,6 +814,7 @@ let PATCH_ORDERS: PatchArray = [
|
|||||||
'disableStreamGate',
|
'disableStreamGate',
|
||||||
'overrideSettings',
|
'overrideSettings',
|
||||||
'broadcastPollingMode',
|
'broadcastPollingMode',
|
||||||
|
getPref(PrefKey.UI_CONTROLLER_FRIENDLY) && 'patchGamepadPolling',
|
||||||
|
|
||||||
'exposeStreamSession',
|
'exposeStreamSession',
|
||||||
'exposeDialogRoutes',
|
'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};
|
const currentGamepad = ${gamepadVar};
|
||||||
|
|
||||||
// Share button on XS controller
|
// Share button on XS controller
|
||||||
|
@ -13,6 +13,7 @@ import { VibrationManager } from "../vibration-manager";
|
|||||||
import { StreamStats } from "./stream-stats";
|
import { StreamStats } from "./stream-stats";
|
||||||
import { BxSelectElement } from "@/web-components/bx-select";
|
import { BxSelectElement } from "@/web-components/bx-select";
|
||||||
import { onChangeVideoPlayerType, updateVideoPlayer } from "./stream-settings-utils";
|
import { onChangeVideoPlayerType, updateVideoPlayer } from "./stream-settings-utils";
|
||||||
|
import { GamepadKey } from "@/enums/mkb";
|
||||||
|
|
||||||
enum FocusDirection {
|
enum FocusDirection {
|
||||||
UP,
|
UP,
|
||||||
@ -38,6 +39,21 @@ export class StreamSettings {
|
|||||||
return StreamSettings.instance;
|
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 $container: HTMLElement | undefined;
|
||||||
private $tabs: HTMLElement | undefined;
|
private $tabs: HTMLElement | undefined;
|
||||||
private $settings: HTMLElement | undefined;
|
private $settings: HTMLElement | undefined;
|
||||||
@ -282,6 +298,12 @@ export class StreamSettings {
|
|||||||
|
|
||||||
// Add event listeners
|
// Add event listeners
|
||||||
$container.addEventListener('keydown', this);
|
$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);
|
BxEvent.dispatch(window, BxEvent.XCLOUD_DIALOG_SHOWN);
|
||||||
@ -298,9 +320,94 @@ export class StreamSettings {
|
|||||||
// Remove event listeners
|
// Remove event listeners
|
||||||
this.$container!.removeEventListener('keydown', this);
|
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);
|
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) {
|
#handleTabsNavigation($focusing: HTMLElement, direction: FocusDirection) {
|
||||||
if (direction === FocusDirection.UP || direction === FocusDirection.DOWN) {
|
if (direction === FocusDirection.UP || direction === FocusDirection.DOWN) {
|
||||||
let $sibling = $focusing;
|
let $sibling = $focusing;
|
||||||
@ -418,7 +525,7 @@ export class StreamSettings {
|
|||||||
case 'keydown':
|
case 'keydown':
|
||||||
const $target = event.target as HTMLElement;
|
const $target = event.target as HTMLElement;
|
||||||
const keyboardEvent = event as KeyboardEvent;
|
const keyboardEvent = event as KeyboardEvent;
|
||||||
const keyCode = keyboardEvent.code;
|
const keyCode = keyboardEvent.code || keyboardEvent.key;
|
||||||
|
|
||||||
if (keyCode === 'ArrowUp' || keyCode === 'ArrowDown') {
|
if (keyCode === 'ArrowUp' || keyCode === 'ArrowDown') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -115,4 +115,6 @@ export const BxExposed = {
|
|||||||
hasCompletedOnboarding: true,
|
hasCompletedOnboarding: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
disableGamepadPolling: false,
|
||||||
};
|
};
|
||||||
|
@ -714,6 +714,7 @@ export class Preferences {
|
|||||||
default: 100,
|
default: 100,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 600,
|
max: 600,
|
||||||
|
steps: 20,
|
||||||
params: {
|
params: {
|
||||||
suffix: '%',
|
suffix: '%',
|
||||||
ticks: 100,
|
ticks: 100,
|
||||||
@ -772,6 +773,7 @@ export class Preferences {
|
|||||||
default: 80,
|
default: 80,
|
||||||
min: 50,
|
min: 50,
|
||||||
max: 100,
|
max: 100,
|
||||||
|
steps: 10,
|
||||||
params: {
|
params: {
|
||||||
suffix: '%',
|
suffix: '%',
|
||||||
ticks: 10,
|
ticks: 10,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user