Compare commits

..

22 Commits

Author SHA1 Message Date
adf689d61d Bump version to 3.2.3 2024-03-10 20:47:21 +07:00
086385fbfb Update translations 2024-03-10 20:45:18 +07:00
705446ffb9 Fix language names 2024-03-10 20:21:45 +07:00
e366cb73e4 Stop exposing Toast class 2024-03-10 18:02:03 +07:00
d148fc708e Minor bug fixes 2024-03-10 18:01:36 +07:00
f75e22b5f6 Move Touch control settings to Controller tab 2024-03-10 17:57:16 +07:00
0f7ac4c372 Don't call MkbHandler.destroy() if the MKB feature is not enabled 2024-03-10 17:45:19 +07:00
a4874c76db Stop redrawing the "👀" emoji every seconds in the stats bar 2024-03-10 17:39:08 +07:00
397b3baa9b Avoid rendering the Settings multiple times 2024-03-10 16:20:53 +07:00
0330d0d811 Shorten name of the MKB controller 2024-03-10 07:54:08 +07:00
f8e035d98f Show a toast when a controller is connected/disconnected 2024-03-09 22:51:42 +07:00
1e7bc366ca Change STREAM_TARGET_RESOLUTION's default value back to "auto" 2024-03-09 21:29:19 +07:00
c207025df9 Bump version to 3.2.2 2024-03-08 17:16:36 +07:00
83b35dfc61 Update translations 2024-03-08 17:06:12 +07:00
e6ec664087 Update styling of disabled <select> 2024-03-08 17:03:23 +07:00
06790c8098 Use "accent-color" style on inputs 2024-03-08 16:50:53 +07:00
7310a009df Update style of the Reload page button 2024-03-08 16:50:25 +07:00
5392414abd Show Settings' reload button at the bottom when modifying settings 2024-03-07 18:36:47 +07:00
d78e55586e Update style of disabled <select> in Settings 2024-03-07 17:46:08 +07:00
77abc44f3d Add "STREAM_TOUCH_CONTROLLER_AUTO_OFF" setting 2024-03-07 17:39:37 +07:00
788ac3d527 Add "disableTakRenderer" patch 2024-03-07 09:12:30 +07:00
47ef5a9cd2 Fix "blockWebRtcStatsCollector" patch and add "blockGamepadStatsCollector" patch 2024-03-06 17:57:18 +07:00
2 changed files with 291 additions and 98 deletions

View File

@ -1,5 +1,5 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 3.2.1
// @version 3.2.3
// ==/UserScript==

View File

@ -1,7 +1,7 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 3.2.1
// @version 3.2.3
// @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx
// @license MIT
@ -14,7 +14,7 @@
// ==/UserScript==
'use strict';
const SCRIPT_VERSION = '3.2.1';
const SCRIPT_VERSION = '3.2.3';
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
const ENABLE_XCLOUD_LOGGER = false;
@ -675,6 +675,22 @@ const Translations = {
"Bạn có muốn kết nối lại stream không?",
"您想要刷新吗?",
],
"connected": [
,
"Connected",
"Conectado",
,
,
"接続済み",
,
"Połączony",
,
"Подключен",
"Bağlı",
"Під’єднано",
"Đã kết nối",
"已连接",
],
"console-connect": [
"Verbinden",
"Connect",
@ -963,6 +979,22 @@ const Translations = {
"Đã tắt",
"禁用",
],
"disconnected": [
,
"Disconnected",
"Desconectado",
,
,
"切断",
,
"Rozłączony",
,
"Отключен",
"Bağlı değil",
"Роз’єднано",
"Đã ngắt kết nối",
"已断开连接",
],
"edit": [
"Bearbeiten",
"Edit",
@ -996,7 +1028,7 @@ const Translations = {
"启用手柄快捷方式",
],
"enable-local-co-op-support": [
"Lokale Coop-Unterstützung aktivieren",
"Lokale Koop-Unterstützung aktivieren",
"Enable local co-op support",
"Habilitar soporte co-op local",
,
@ -1009,7 +1041,7 @@ const Translations = {
"Yerel çok oyuncu desteğini aktive et",
"Увімкнути локальну co-op підтримку",
"Kích hoạt tính năng chơi chung cục bộ",
,
"启用本地多人联机",
],
"enable-local-co-op-support-note": [
"Funktioniert nur, wenn das Spiel kein anderes Profil benötigt",
@ -1025,7 +1057,7 @@ const Translations = {
"Bu seçenek ancak oyun ayrı profillere giriş yapılmasını istemiyorsa etki eder",
"Працює, лише якщо для гри не потрібен інший профіль",
"Chỉ hoạt động nếu game không yêu cầu thêm tài khoản khác",
,
"仅在当前游戏不要求切换账户时才能使用",
],
"enable-mic-on-startup": [
"Mikrofon bei Spielstart aktivieren",
@ -1045,18 +1077,18 @@ const Translations = {
],
"enable-mkb": [
"Maus- und Tastaturunterstützung aktivieren",
"Enable Mouse & Keyboard support",
"Habilitar soporte para ratón y teclado",
"Emulate controller with Mouse & Keyboard",
"Emular mandos con teclado y ratón",
,
"Abilitare il supporto di mouse e tastiera",
"マウス&キーボードのサポートを有効化",
"マウス&キーボード操作をコントローラー化",
"마우스 & 키보드 활성화",
"Włącz obsługę myszy i klawiatury",
"Habilitar suporte ao Mouse & Teclado",
"Включить поддержку мыши и клавиатуры",
"Эмулировать контроллер с помощью мыши и клавиатуры",
"Klavye ve fare desteğini aktive et",
"Увімкнути підтримку миші та клавіатури",
"Kích hoạt hỗ trợ Chuột & Bàn phím",
"Емуляція контролера за допомогою миші та клавіатури",
"Giả lập tay cầm bằng Chuột Bàn phím",
"启用鼠标和键盘支持",
],
"enable-quick-glance-mode": [
@ -1169,7 +1201,7 @@ const Translations = {
"Mobil cihazda Fortnite: Dünyayı Kurtar modunu etkinleştir",
"Дозволити відтворення режиму STW на мобільному пристрої",
"Cho phép chơi chế độ STW trên điện thoại",
,
"允许游玩Save the World模式",
],
"fortnite-force-console-version": [
"Fortnite: Erzwinge Konsolenversion",
@ -1185,7 +1217,7 @@ const Translations = {
"Fortnite'ın konsol sürümünü aç",
"Fortnite: примусова консольна версія",
"Fortnite: bắt buộc phiên bản console",
,
"Fortnite: 强制使用主机版客户端",
],
"getting-consoles-list": [
"Rufe Liste der Konsolen ab...",
@ -1380,20 +1412,20 @@ const Translations = {
"载入画面",
],
"local-co-op": [
,
"Lokales Koop",
"Local co-op",
"Co-op local",
,
,
"ローカルマルチプレイ",
,
,
"Lokalna kooperacja",
"Co-op local",
,
,
"Локальная кооперативная игра",
"Yerel çoklu oyunculu",
"Локальний co-op",
"Chơi chung cục bộ",
,
"本地多人联机",
],
"map-mouse-to": [
"Maus binden an",
@ -2123,13 +2155,13 @@ const Translations = {
,
"タッチコントローラーとコントローラー#1を分ける",
,
,
"Oddziel Kontroler dotykowy i Kontroler #1",
"Separar o Controle por Toque e o Controle #1",
"Раздельный сенсорный контроллер и контроллер #1",
,
"Dokunmatik kumandayı ve birincil kumandayı ayrı tut",
"Окремо Сенсорний контролер та Контролер #1",
"Tách biệt Bộ điều khiển cảm ứng và Tay cầm #1",
,
"虚拟摇杆和手柄分别控制不同角色",
],
"separate-touch-controller-note": [
"Touch-Controller ist Spieler 1, Controller #1 ist Spieler 2",
@ -2139,13 +2171,13 @@ const Translations = {
,
"タッチコントローラーがプレイヤー1、コントローラー#1がプレイヤー2に割り当てられます",
,
,
"Kontroler dotykowy to Gracz 1, Kontroler #1 to Gracz 2",
"O Controle por Toque é o Jogador 1, o Controle #1 é o Jogador 2",
"Сенсорный контроллер — игрок 1, контроллер #1 — игрок 2",
,
"Dokunmaktik kumanda birinci oyuncu, birincil kumanda ikinci oyuncu",
"Сенсорний контролер це Гравець 1, Контролер #1 це Гравець 2",
"Bộ điều khiển cảm ứng là Người chơi 1, Tay cầm #1 là Người chơi 2",
,
"虚拟摇杆为玩家1手柄#1为玩家2",
],
"server": [
"Server",
@ -2627,6 +2659,22 @@ const Translations = {
"Trắng hoàn toàn",
"白色",
],
"tc-auto-off": [
"Aus, wenn Controller gefunden",
"Off when controller found",
"Desactivar cuando se encuentra el controlador",
,
,
"コントローラー接続時に無効化",
,
"Wyłącz, gdy kontroler zostanie znaleziony",
"Desligar toque quando o controle estiver conectado",
"Выключить, когда контроллер найден",
"Başka bir kumanda bağlandığında kapat",
"Вимкнено, коли контролер знайдено",
"Tắt khi sử dụng tay cầm",
"手柄连接时隐藏虚拟摇杆",
],
"tc-availability": [
"Verfügbarkeit",
"Availability",
@ -3061,7 +3109,7 @@ const Translations = {
],
}
const LOCALE = Translations.getLocale();
let LOCALE = Translations.getLocale();
const t = Translations.get;
@ -4047,14 +4095,39 @@ class Toast {
static #$wrapper;
static #$msg;
static #$status;
static #stack = [];
static #isShowing = false;
static #timeout;
static #DURATION = 3000;
static show(msg, status) {
static show(msg, status, options) {
options = options || {};
if (options.instant) {
// Clear stack
Toast.#stack = [arguments];
Toast.#showNext();
} else {
Toast.#stack.push(arguments);
!Toast.#isShowing && Toast.#showNext();
}
}
static #showNext() {
if (!Toast.#stack.length) {
Toast.#isShowing = false;
return;
}
Toast.#isShowing = true;
Toast.#timeout && clearTimeout(Toast.#timeout);
Toast.#timeout = setTimeout(Toast.#hide, Toast.#DURATION);
// Get values from item
const [msg, status, options] = Toast.#stack.shift();
Toast.#$msg.textContent = msg;
if (status) {
@ -4087,6 +4160,8 @@ class Toast {
if (classList.contains('bx-hide')) {
classList.remove('bx-offscreen', 'bx-hide');
classList.add('bx-offscreen');
Toast.#showNext();
}
});
@ -4831,7 +4906,7 @@ class MkbHandler {
static get MAXIMUM_STICK_RANGE() { return 1.1; }
#VIRTUAL_GAMEPAD = {
id: 'Xbox 360 Controller (XInput STANDARD GAMEPAD)',
id: 'Xbox 360 Controller',
index: 3,
connected: false,
hapticActuators: null,
@ -6712,6 +6787,7 @@ class Preferences {
static get STREAM_SIMPLIFY_MENU() { return 'stream_simplify_menu'; }
static get STREAM_TOUCH_CONTROLLER() { return 'stream_touch_controller'; }
static get STREAM_TOUCH_CONTROLLER_AUTO_OFF() { return 'stream_touch_controller_auto_off'; }
static get STREAM_TOUCH_CONTROLLER_STYLE_STANDARD() { return 'stream_touch_controller_style_standard'; }
static get STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM() { return 'stream_touch_controller_style_custom'; }
@ -6785,18 +6861,18 @@ class Preferences {
'options': {
'de-DE': 'Deutsch',
'en-US': 'English (United States)',
'es-ES': 'espa\xf1ol (Espa\xf1a)',
'fr-FR': 'fran\xe7ais',
'es-ES': 'español (España)',
'fr-FR': 'français',
'it-IT': 'italiano',
'ja-JP': '\u65e5\u672c\u8a9e',
'ko-KR': '\ud55c\uad6d\uc5b4',
'ja-JP': '日本語',
'ko-KR': '한국어',
'pl-PL': 'polski',
'pt-BR': 'portugu\xeas (Brasil)',
'ru-RU': '\u0440\u0443\u0441\u0441\u043a\u0438\u0439',
'tr-TR': 'T\xfcrk\xe7e',
'pt-BR': 'português (Brasil)',
'ru-RU': 'русский',
'tr-TR': 'Türkçe',
'uk-UA': 'українська',
'vi-VN': 'Tiếng Việt',
'zh-CN': '\u4e2d\u6587(\u7b80\u4f53)',
'zh-CN': '中文(简体)',
},
},
[Preferences.SERVER_REGION]: {
@ -6806,37 +6882,37 @@ class Preferences {
'default': 'default',
'options': {
'default': t('default'),
'ar-SA': '\u0627\u0644\u0639\u0631\u0628\u064a\u0629',
'cs-CZ': '\u010de\u0161tina',
'ar-SA': 'العربية',
'cs-CZ': 'čeština',
'da-DK': 'dansk',
'de-DE': 'Deutsch',
'el-GR': '\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac',
'el-GR': 'Ελληνικά',
'en-GB': 'English (United Kingdom)',
'en-US': 'English (United States)',
'es-ES': 'espa\xf1ol (Espa\xf1a)',
'es-MX': 'espa\xf1ol (Latinoam\xe9rica)',
'es-ES': 'español (España)',
'es-MX': 'español (Latinoamérica)',
'fi-FI': 'suomi',
'fr-FR': 'fran\xe7ais',
'he-IL': '\u05e2\u05d1\u05e8\u05d9\u05ea',
'fr-FR': 'français',
'he-IL': 'עברית',
'hu-HU': 'magyar',
'it-IT': 'italiano',
'ja-JP': '\u65e5\u672c\u8a9e',
'ko-KR': '\ud55c\uad6d\uc5b4',
'nb-NO': 'norsk bokm\xe5l',
'ja-JP': '日本語',
'ko-KR': '한국어',
'nb-NO': 'norsk bokmål',
'nl-NL': 'Nederlands',
'pl-PL': 'polski',
'pt-BR': 'portugu\xeas (Brasil)',
'pt-PT': 'portugu\xeas (Portugal)',
'ru-RU': '\u0440\u0443\u0441\u0441\u043a\u0438\u0439',
'sk-SK': 'sloven\u010dina',
'pt-BR': 'português (Brasil)',
'pt-PT': 'português (Portugal)',
'ru-RU': 'русский',
'sk-SK': 'slovenčina',
'sv-SE': 'svenska',
'tr-TR': 'T\xfcrk\xe7e',
'zh-CN': '\u4e2d\u6587(\u7b80\u4f53)',
'zh-TW': '\u4e2d\u6587 (\u7e41\u9ad4)',
'tr-TR': 'Türkçe',
'zh-CN': '中文(简体)',
'zh-TW': '中文 (繁體)',
},
},
[Preferences.STREAM_TARGET_RESOLUTION]: {
'default': '1080p',
'default': 'auto',
'options': {
'auto': t('default'),
'1080p': '1080p',
@ -6929,21 +7005,25 @@ class Preferences {
[Preferences.HIDE_DOTS_ICON]: {
'default': false,
},
[Preferences.STREAM_TOUCH_CONTROLLER]: {
'default': 'all',
'options': {
'default': t('default'),
'all': t('tc-all-games'),
'off': t('off'),
},
'unsupported': !HAS_TOUCH_SUPPORT,
'ready': () => {
const setting = Preferences.SETTINGS[Preferences.STREAM_TOUCH_CONTROLLER];
if (setting.unsupported) {
setting.default = 'off';
setting.default = 'default';
}
},
},
[Preferences.STREAM_TOUCH_CONTROLLER_AUTO_OFF]: {
'default': false,
'unsupported': !HAS_TOUCH_SUPPORT,
},
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: {
'default': 'default',
'options': {
@ -6961,6 +7041,7 @@ class Preferences {
},
'unsupported': !HAS_TOUCH_SUPPORT,
},
[Preferences.STREAM_SIMPLIFY_MENU]: {
'default': false,
},
@ -7482,12 +7563,23 @@ class Patcher {
// Block WebRTC stats collector
blockWebRtcStatsCollector: function(funcStr) {
const text = 'this.intervalMs=0,';
const text = 'this.shouldCollectStats=!0';
if (!funcStr.includes(text)) {
return false;
}
return funcStr.replace(text, 'false,' + text);
return funcStr.replace(text, 'this.shouldCollectStats=!1');
},
blockGamepadStatsCollector: function(funcStr) {
const text = 'this.inputPollingIntervalStats.addValue';
if (!funcStr.includes(text)) {
return false;
}
funcStr = funcStr.replace('this.inputPollingIntervalStats.addValue', '');
funcStr = funcStr.replace('this.inputPollingDurationStats.addValue', '');
return funcStr;
},
enableXcloudLogger: function(funcStr) {
@ -7690,6 +7782,32 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
funcStr = funcStr.replace(text, text + newCode);
return funcStr;
},
disableTakRenderer: function(funcStr) {
const text = 'const{TakRenderer:';
if (!funcStr.includes(text)) {
return false;
}
const newCode = `
const gamepads = window.navigator.getGamepads();
let gamepadFound = false;
for (let gamepad of gamepads) {
if (gamepad && gamepad.connected) {
gamepadFound = true;
break;
}
}
if (gamepadFound) {
return;
}
`;
funcStr = funcStr.replace(text, newCode + text);
return funcStr;
},
};
static #PATCH_ORDERS = [
@ -7733,10 +7851,13 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
getPref(Preferences.REMOTE_PLAY_ENABLED) && ['remotePlayConnectMode'],
['playVibration'],
getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'all' && ['exposeTouchLayoutManager'],
HAS_TOUCH_SUPPORT && getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'all' && ['exposeTouchLayoutManager'],
HAS_TOUCH_SUPPORT && getPref(Preferences.STREAM_TOUCH_CONTROLLER_AUTO_OFF) && ['disableTakRenderer'],
ENABLE_XCLOUD_LOGGER && ['enableConsoleLogging'],
getPref(Preferences.BLOCK_TRACKING) && ['blockGamepadStatsCollector'],
[
'disableGamepadDisconnectedScreen',
ENABLE_NATIVE_MKB_BETA && 'mkbMouseAndKeyboardEnabled',
@ -7979,6 +8100,7 @@ function addCss() {
--bx-danger-button-disabled-color: #a26c6c;
--bx-toast-z-index: 9999;
--bx-reload-button-z-index: 9200;
--bx-dialog-z-index: 9101;
--bx-dialog-overlay-z-index: 9100;
--bx-stats-bar-z-index: 9001;
@ -8128,7 +8250,23 @@ a.bx-button {
bottom: 0;
}
.better_xcloud_settings {
.bx-settings-reload-button-wrapper {
z-index: var(--bx-reload-button-z-index);
position: fixed;
bottom: 0;
left: 0;
right: 0;
text-align: center;
background: #000000cf;
padding: 10px;
}
.bx-settings-reload-button-wrapper button {
max-width: 450px;
margin: 0 !important;
}
.bx-settings-container {
background-color: #151515;
user-select: none;
-webkit-user-select: none;
@ -8271,6 +8409,15 @@ a.bx-button {
.bx-settings-row input {
align-self: center;
accent-color: var(--bx-primary-button-color);
}
.bx-settings-row select:disabled {
-webkit-appearance: none;
background: transparent;
text-align-last: right;
border: none;
color: #fff;
}
.bx-settings-wrapper .bx-button.bx-primary {
@ -8423,9 +8570,15 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
border-right: none;
}
.bx-stats-bar[data-display=glancing]::before {
content: '👀 ';
.bx-stats-bar::before {
display: none;
content: '👀';
vertical-align: middle;
margin-right: 8px;
}
.bx-stats-bar[data-display=glancing]::before {
display: inline-block;
}
.bx-stats-bar[data-position=top-left] {
@ -8687,6 +8840,10 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
margin-bottom: 0 !important;
}
.bx-quick-settings-row input {
accent-color: var(--bx-primary-button-color);
}
.bx-quick-settings-tab-contents h2 {
margin-bottom: 8px;
display: flex;
@ -8818,13 +8975,19 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
}
.bx-mkb-settings select:disabled {
-webkit-appearance: none;
background: transparent;
text-align-last: right;
text-align: right;
border: none;
color: #fff;
}
.bx-quick-settings-row select:disabled {
text-align: right;
-webkit-appearance: none;
background: transparent;
text-align-last: right;
border: none;
}
.bx-mkb-pointer-lock-msg {
@ -9058,11 +9221,6 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
font-size: 12px;
}
.bx-remote-play-settings input {
display: block;
margin: 0 auto;
}
.bx-remote-play-settings span {
font-weight: bold;
font-size: 18px;
@ -9181,15 +9339,6 @@ div[class*=StreamHUD-module__buttonsContainer] {
`;
}
// Hide touch controller
if (getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'off') {
css += `
#MultiTouchSurface, #BabylonCanvasContainer-main {
display: none !important;
}
`;
}
// Simplify Stream's menu
css += `
div[class*=StreamMenu-module__menu] {
@ -9806,6 +9955,7 @@ function injectSettingsButton($parent) {
const PREF_LATEST_VERSION = getPref(Preferences.LATEST_VERSION);
const $headerFragment = document.createDocumentFragment();
let $reloadBtnWrapper;
// Remote Play button
if (getPref(Preferences.REMOTE_PLAY_ENABLED)) {
@ -9828,7 +9978,7 @@ function injectSettingsButton($parent) {
label: PREF_PREFERRED_REGION,
style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_HEIGHT,
onClick: e => {
const $settings = document.querySelector('.better_xcloud_settings');
const $settings = document.querySelector('.bx-settings-container');
$settings.classList.toggle('bx-gone');
$settings.scrollIntoView();
document.activeElement && document.activeElement.blur();
@ -9842,12 +9992,16 @@ function injectSettingsButton($parent) {
// Add the Settings button to the web page
$headerFragment.appendChild($settingsBtn);
$parent.appendChild($headerFragment);
// Avoid rendering the Settings multiple times
if (document.querySelector('.bx-settings-container')) {
return;
}
// Setup Settings UI
const $container = CE('div', {
'class': 'better_xcloud_settings bx-gone',
'class': 'bx-settings-container bx-gone',
});
let $updateAvailable;
@ -9919,6 +10073,7 @@ function injectSettingsButton($parent) {
[t('touch-controller')]: {
_note: !HAS_TOUCH_SUPPORT ? '⚠️ ' + t('device-unsupported-touch') : null,
[Preferences.STREAM_TOUCH_CONTROLLER]: t('tc-availability'),
[Preferences.STREAM_TOUCH_CONTROLLER_AUTO_OFF]: t('tc-auto-off'),
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: t('tc-standard-layout-style'),
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM]: t('tc-custom-layout-style'),
},
@ -9956,6 +10111,23 @@ function injectSettingsButton($parent) {
$wrapper.appendChild($group);
let onChange = e => {
if (!$reloadBtnWrapper) {
return;
}
$reloadBtnWrapper.classList.remove('bx-gone');
if (e.target.id === 'bx_setting_' + Preferences.BETTER_XCLOUD_LOCALE) {
// Update locale
LOCALE = Translations.getLocale();
const $btn = $reloadBtnWrapper.firstElementChild;
$btn.textContent = t('settings-reloading');
$btn.click();
}
};
for (let settingId in SETTINGS_UI[groupLabel]) {
if (settingId.startsWith('_')) {
continue;
@ -9978,6 +10150,7 @@ function injectSettingsButton($parent) {
});
$inpCustomUserAgent.addEventListener('change', e => {
setPref(Preferences.USER_AGENT_CUSTOM, e.target.value.trim());
onChange(e);
});
$control = PREFS.toElement(Preferences.USER_AGENT_PROFILE, e => {
@ -9988,6 +10161,8 @@ function injectSettingsButton($parent) {
$inpCustomUserAgent.value = userAgent;
$inpCustomUserAgent.readOnly = !isCustom;
$inpCustomUserAgent.disabled = !isCustom;
onChange(e);
});
} else if (settingId === Preferences.SERVER_REGION) {
let selectedValue;
@ -9995,6 +10170,7 @@ function injectSettingsButton($parent) {
$control = CE('select', {id: `bx_setting_${settingId}`});
$control.addEventListener('change', e => {
setPref(settingId, e.target.value);
onChange(e);
});
selectedValue = PREF_PREFERRED_REGION;
@ -10020,15 +10196,14 @@ function injectSettingsButton($parent) {
$control.appendChild($option);
}
} else {
let onChange = null;
if (settingId === Preferences.BETTER_XCLOUD_LOCALE) {
onChange = e => {
$control = PREFS.toElement(settingId, e => {
localStorage.setItem('better_xcloud_locale', e.target.value);
window.location.reload();
}
onChange(e);
});
} else {
$control = PREFS.toElement(settingId, onChange);
}
$control = PREFS.toElement(settingId, onChange);
labelAttrs = {'for': $control.id, 'tabindex': 0};
}
@ -10059,9 +10234,8 @@ function injectSettingsButton($parent) {
// Setup Reload button
const $reloadBtn = createButton({
classes: ['bx-settings-reload-button'],
label: t('settings-reload'),
style: ButtonStyle.PRIMARY | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_WIDTH,
style: ButtonStyle.DANGER | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_WIDTH,
onClick: e => {
window.location.reload();
$reloadBtn.disabled = true;
@ -10069,7 +10243,9 @@ function injectSettingsButton($parent) {
},
});
$reloadBtn.setAttribute('tabindex', 0);
$wrapper.appendChild($reloadBtn);
$reloadBtnWrapper = CE('div', {'class': 'bx-settings-reload-button-wrapper bx-gone'}, $reloadBtn);
$wrapper.appendChild($reloadBtnWrapper);
// Donation link
const $donationLink = CE('a', {'class': 'bx-donation-link', href: 'https://ko-fi.com/redphx', target: '_blank'}, `❤️ ${t('support-better-xcloud')}`);
@ -10667,14 +10843,8 @@ function setupQuickSettingsBar() {
},
],
},
],
},
HAS_TOUCH_SUPPORT && {
icon: Icon.HAND_TAP,
group: 'touch-controller',
items: [
{
HAS_TOUCH_SUPPORT && {
group: 'touch-controller',
label: t('touch-controller'),
items: [
@ -10833,6 +11003,10 @@ function setupQuickSettingsBar() {
const $group = CE('div', {'data-group': settingTab.group, 'class': 'bx-gone'});
for (const settingGroup of settingTab.items) {
if (!settingGroup) {
continue;
}
$group.appendChild(CE('h2', {},
CE('span', {}, settingGroup.label),
settingGroup.help_url && createButton({
@ -10990,7 +11164,7 @@ function onHistoryChanged(e) {
setTimeout(RemotePlay.detect, 10);
const $settings = document.querySelector('.better_xcloud_settings');
const $settings = document.querySelector('.bx-settings-container');
if ($settings) {
$settings.classList.add('bx-gone');
}
@ -11105,7 +11279,7 @@ window.addEventListener(BxEvent.STREAM_STOPPED, e => {
IS_PLAYING = false;
// Stop MKB listeners
MkbHandler.INSTANCE.destroy();
getPref(Preferences.MKB_ENABLED) && MkbHandler.INSTANCE.destroy();
const $quickBar = document.querySelector('.bx-quick-settings-bar');
if ($quickBar) {
@ -11231,3 +11405,22 @@ RemotePlay.detect();
StreamBadges.setupEvents();
StreamStats.setupEvents();
MkbHandler.setupEvents();
// Show a toast when connecting/disconecting controller
function showGamepadToast(gamepad) {
let text = '🎮';
if (getPref(Preferences.LOCAL_CO_OP_ENABLED)) {
text += ` #${gamepad.index + 1}`;
}
// Remove "(STANDARD GAMEPAD Vendor: xxx Product: xxx)" from ID
const gamepadId = gamepad.id.replace(/ \(.* Vendor: \w+ Product: \w+\)$/, '');
text += ` - ${gamepadId}`;
const status = gamepad.connected ? t('connected') : t('disconnected');
Toast.show(text, status, {instant: false});
}
window.addEventListener('gamepadconnected', e => showGamepadToast(e.gamepad));
window.addEventListener('gamepaddisconnected', e => showGamepadToast(e.gamepad));