mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-07-07 23:01:44 +02:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
3d34bb3edf | |||
ab1c93eb3a | |||
739adfce41 | |||
2e77f19006 | |||
8a40d361d9 | |||
98fa273b48 | |||
1e6527413c | |||
b9134bc141 | |||
336a965653 |
2
build.ts
2
build.ts
@ -139,7 +139,7 @@ const build = async (target: BuildTarget, version: string, variant: BuildVariant
|
|||||||
await Bun.write(path, scriptHeader + result);
|
await Bun.write(path, scriptHeader + result);
|
||||||
|
|
||||||
// Create meta file (don't build if it's beta version)
|
// Create meta file (don't build if it's beta version)
|
||||||
if (!version.includes('beta')) {
|
if (!version.includes('beta') && variant === 'full') {
|
||||||
await Bun.write(outDir + '/' + outputMetaName, txtMetaHeader.replace('[[VERSION]]', version));
|
await Bun.write(outDir + '/' + outputMetaName, txtMetaHeader.replace('[[VERSION]]', version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5
dist/better-xcloud.lite.meta.js
vendored
5
dist/better-xcloud.lite.meta.js
vendored
@ -1,5 +0,0 @@
|
|||||||
// ==UserScript==
|
|
||||||
// @name Better xCloud
|
|
||||||
// @namespace https://github.com/redphx
|
|
||||||
// @version 5.8.0
|
|
||||||
// ==/UserScript==
|
|
63
dist/better-xcloud.lite.user.js
vendored
63
dist/better-xcloud.lite.user.js
vendored
@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Better xCloud (Lite)
|
// @name Better xCloud (Lite)
|
||||||
// @namespace https://github.com/redphx
|
// @namespace https://github.com/redphx
|
||||||
// @version 5.8.0
|
// @version 5.8.1
|
||||||
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
||||||
// @author redphx
|
// @author redphx
|
||||||
// @license MIT
|
// @license MIT
|
||||||
@ -118,9 +118,9 @@ function deepClone(obj) {
|
|||||||
if (!obj) return {};
|
if (!obj) return {};
|
||||||
return JSON.parse(JSON.stringify(obj));
|
return JSON.parse(JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
var SCRIPT_VERSION = "5.8.0", SCRIPT_VARIANT = "lite", AppInterface = window.AppInterface;
|
var SCRIPT_VERSION = "5.8.1", SCRIPT_VARIANT = "lite", AppInterface = window.AppInterface;
|
||||||
UserAgent.init();
|
UserAgent.init();
|
||||||
var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, STATES = {
|
var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, supportMkb = AppInterface || !userAgent.match(/(android|iphone|ipad)/), STATES = {
|
||||||
supportedRegion: !0,
|
supportedRegion: !0,
|
||||||
serverRegions: {},
|
serverRegions: {},
|
||||||
selectedRegion: {},
|
selectedRegion: {},
|
||||||
@ -137,7 +137,8 @@ var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.inclu
|
|||||||
userAgent: {
|
userAgent: {
|
||||||
isTv,
|
isTv,
|
||||||
capabilities: {
|
capabilities: {
|
||||||
touch: userAgentHasTouchSupport
|
touch: userAgentHasTouchSupport,
|
||||||
|
mkb: supportMkb
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
currentStream: {},
|
currentStream: {},
|
||||||
@ -326,10 +327,11 @@ var SUPPORTED_LANGUAGES = {
|
|||||||
disabled: "Disabled",
|
disabled: "Disabled",
|
||||||
disconnected: "Disconnected",
|
disconnected: "Disconnected",
|
||||||
download: "Download",
|
download: "Download",
|
||||||
|
downloaded: "Downloaded",
|
||||||
edit: "Edit",
|
edit: "Edit",
|
||||||
"enable-controller-shortcuts": "Enable controller shortcuts",
|
"enable-controller-shortcuts": "Enable controller shortcuts",
|
||||||
"enable-local-co-op-support": "Enable local co-op support",
|
"enable-local-co-op-support": "Enable local co-op support",
|
||||||
"enable-local-co-op-support-note": "Only works if the game doesn't require a different profile",
|
"enable-local-co-op-support-note": "Only works with some games",
|
||||||
"enable-mic-on-startup": "Enable microphone on game launch",
|
"enable-mic-on-startup": "Enable microphone on game launch",
|
||||||
"enable-mkb": "Emulate controller with Mouse & Keyboard",
|
"enable-mkb": "Emulate controller with Mouse & Keyboard",
|
||||||
"enable-quick-glance-mode": "Enable \"Quick Glance\" mode",
|
"enable-quick-glance-mode": "Enable \"Quick Glance\" mode",
|
||||||
@ -339,7 +341,7 @@ var SUPPORTED_LANGUAGES = {
|
|||||||
experimental: "Experimental",
|
experimental: "Experimental",
|
||||||
export: "Export",
|
export: "Export",
|
||||||
fast: "Fast",
|
fast: "Fast",
|
||||||
"fortnite-allow-stw-mode": "Allows playing STW mode on mobile",
|
"fortnite-allow-stw-mode": "Allows playing \"Save the World\" mode on mobile",
|
||||||
"fortnite-force-console-version": "Fortnite: force console version",
|
"fortnite-force-console-version": "Fortnite: force console version",
|
||||||
"game-bar": "Game Bar",
|
"game-bar": "Game Bar",
|
||||||
"getting-consoles-list": "Getting the list of consoles...",
|
"getting-consoles-list": "Getting the list of consoles...",
|
||||||
@ -382,6 +384,7 @@ var SUPPORTED_LANGUAGES = {
|
|||||||
"mkb-disclaimer": "Using this feature when playing online could be viewed as cheating",
|
"mkb-disclaimer": "Using this feature when playing online could be viewed as cheating",
|
||||||
"mouse-and-keyboard": "Mouse & Keyboard",
|
"mouse-and-keyboard": "Mouse & Keyboard",
|
||||||
"mouse-wheel": "Mouse wheel",
|
"mouse-wheel": "Mouse wheel",
|
||||||
|
"msfs2020-force-native-mkb": "MSFS2020: force native M&KB support",
|
||||||
muted: "Muted",
|
muted: "Muted",
|
||||||
name: "Name",
|
name: "Name",
|
||||||
"native-mkb": "Native Mouse & Keyboard",
|
"native-mkb": "Native Mouse & Keyboard",
|
||||||
@ -581,6 +584,7 @@ var SUPPORTED_LANGUAGES = {
|
|||||||
unmuted: "Unmuted",
|
unmuted: "Unmuted",
|
||||||
"unsharp-masking": "Unsharp masking",
|
"unsharp-masking": "Unsharp masking",
|
||||||
upload: "Upload",
|
upload: "Upload",
|
||||||
|
uploaded: "Uploaded",
|
||||||
"use-mouse-absolute-position": "Use mouse's absolute position",
|
"use-mouse-absolute-position": "Use mouse's absolute position",
|
||||||
"use-this-at-your-own-risk": "Use this at your own risk",
|
"use-this-at-your-own-risk": "Use this at your own risk",
|
||||||
"user-agent-profile": "User-Agent profile",
|
"user-agent-profile": "User-Agent profile",
|
||||||
@ -1192,7 +1196,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
|
|||||||
options: getSupportedCodecProfiles(),
|
options: getSupportedCodecProfiles(),
|
||||||
ready: (setting) => {
|
ready: (setting) => {
|
||||||
const options = setting.options, keys = Object.keys(options);
|
const options = setting.options, keys = Object.keys(options);
|
||||||
if (keys.length <= 1) setting.unsupported = !0, setting.note = "⚠️ " + t("browser-unsupported-feature");
|
if (keys.length <= 1) setting.unsupported = !0, setting.unsupportedNote = "⚠️ " + t("browser-unsupported-feature");
|
||||||
setting.suggest = {
|
setting.suggest = {
|
||||||
lowest: keys.length === 1 ? keys[0] : keys[1],
|
lowest: keys.length === 1 ? keys[0] : keys[1],
|
||||||
highest: keys[keys.length - 1]
|
highest: keys[keys.length - 1]
|
||||||
@ -1372,15 +1376,12 @@ class GlobalSettingsStorage extends BaseSettingsStore {
|
|||||||
requiredVariants: "full",
|
requiredVariants: "full",
|
||||||
label: t("enable-mkb"),
|
label: t("enable-mkb"),
|
||||||
default: !1,
|
default: !1,
|
||||||
unsupported: (() => {
|
unsupported: !STATES.userAgent.capabilities.mkb,
|
||||||
const userAgent2 = (window.navigator.orgUserAgent || window.navigator.userAgent || "").toLowerCase();
|
|
||||||
return !AppInterface && userAgent2.match(/(android|iphone|ipad)/) ? t("browser-unsupported-feature") : !1;
|
|
||||||
})(),
|
|
||||||
ready: (setting) => {
|
ready: (setting) => {
|
||||||
let note, url;
|
let note, url;
|
||||||
if (setting.unsupported) note = t("browser-unsupported-feature"), url = "https://github.com/redphx/better-xcloud/issues/206#issuecomment-1920475657";
|
if (setting.unsupported) note = t("browser-unsupported-feature"), url = "https://github.com/redphx/better-xcloud/issues/206#issuecomment-1920475657";
|
||||||
else note = t("mkb-disclaimer"), url = "https://better-xcloud.github.io/mouse-and-keyboard/#disclaimer";
|
else note = t("mkb-disclaimer"), url = "https://better-xcloud.github.io/mouse-and-keyboard/#disclaimer";
|
||||||
setting.note = CE("a", {
|
setting.unsupportedNote = CE("a", {
|
||||||
href: url,
|
href: url,
|
||||||
target: "_blank"
|
target: "_blank"
|
||||||
}, "⚠️ " + note);
|
}, "⚠️ " + note);
|
||||||
@ -1662,8 +1663,8 @@ class GlobalSettingsStorage extends BaseSettingsStore {
|
|||||||
dt: `${"dt".toUpperCase()}: ${t("stat-decode-time")}`,
|
dt: `${"dt".toUpperCase()}: ${t("stat-decode-time")}`,
|
||||||
pl: `${"pl".toUpperCase()}: ${t("stat-packets-lost")}`,
|
pl: `${"pl".toUpperCase()}: ${t("stat-packets-lost")}`,
|
||||||
fl: `${"fl".toUpperCase()}: ${t("stat-frames-lost")}`,
|
fl: `${"fl".toUpperCase()}: ${t("stat-frames-lost")}`,
|
||||||
dl: `${"dl".toUpperCase()}: ${t("download")}`,
|
dl: `${"dl".toUpperCase()}: ${t("downloaded")}`,
|
||||||
ul: `${"ul".toUpperCase()}: ${t("upload")}`
|
ul: `${"ul".toUpperCase()}: ${t("uploaded")}`
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
size: 6
|
size: 6
|
||||||
@ -1737,6 +1738,12 @@ class GlobalSettingsStorage extends BaseSettingsStore {
|
|||||||
label: "🎮 " + t("fortnite-force-console-version"),
|
label: "🎮 " + t("fortnite-force-console-version"),
|
||||||
default: !1,
|
default: !1,
|
||||||
note: t("fortnite-allow-stw-mode")
|
note: t("fortnite-allow-stw-mode")
|
||||||
|
},
|
||||||
|
game_msfs2020_force_native_mkb: {
|
||||||
|
requiredVariants: "full",
|
||||||
|
label: "✈️ " + t("msfs2020-force-native-mkb"),
|
||||||
|
default: !1,
|
||||||
|
note: t("may-not-work-properly")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -1824,11 +1831,11 @@ class StreamStats {
|
|||||||
$element: CE("span")
|
$element: CE("span")
|
||||||
},
|
},
|
||||||
dl: {
|
dl: {
|
||||||
name: t("download"),
|
name: t("downloaded"),
|
||||||
$element: CE("span")
|
$element: CE("span")
|
||||||
},
|
},
|
||||||
ul: {
|
ul: {
|
||||||
name: t("upload"),
|
name: t("uploaded"),
|
||||||
$element: CE("span")
|
$element: CE("span")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -3262,8 +3269,14 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
requiredVariants: "full",
|
requiredVariants: "full",
|
||||||
group: "mkb",
|
group: "mkb",
|
||||||
label: t("mouse-and-keyboard"),
|
label: t("mouse-and-keyboard"),
|
||||||
|
unsupportedNote: !STATES.userAgent.capabilities.mkb ? CE("a", {
|
||||||
|
href: "https://github.com/redphx/better-xcloud/issues/206#issuecomment-1920475657",
|
||||||
|
target: "_blank"
|
||||||
|
}, "⚠️ " + t("browser-unsupported-feature")) : null,
|
||||||
|
unsupported: !STATES.userAgent.capabilities.mkb,
|
||||||
items: [
|
items: [
|
||||||
"native_mkb_enabled",
|
"native_mkb_enabled",
|
||||||
|
"game_msfs2020_force_native_mkb",
|
||||||
"mkb_enabled",
|
"mkb_enabled",
|
||||||
"mkb_hide_idle_cursor"
|
"mkb_hide_idle_cursor"
|
||||||
]
|
]
|
||||||
@ -3271,8 +3284,8 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
requiredVariants: "full",
|
requiredVariants: "full",
|
||||||
group: "touch-control",
|
group: "touch-control",
|
||||||
label: t("touch-controller"),
|
label: t("touch-controller"),
|
||||||
note: !STATES.userAgent.capabilities.touch ? "⚠️ " + t("device-unsupported-touch") : null,
|
|
||||||
unsupported: !STATES.userAgent.capabilities.touch,
|
unsupported: !STATES.userAgent.capabilities.touch,
|
||||||
|
unsupportedNote: !STATES.userAgent.capabilities.touch ? "⚠️ " + t("device-unsupported-touch") : null,
|
||||||
items: [
|
items: [
|
||||||
"stream_touch_controller",
|
"stream_touch_controller",
|
||||||
"stream_touch_controller_auto_off",
|
"stream_touch_controller_auto_off",
|
||||||
@ -3472,7 +3485,7 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
requiredVariants: "full",
|
requiredVariants: "full",
|
||||||
group: "native-mkb",
|
group: "native-mkb",
|
||||||
label: t("native-mkb"),
|
label: t("native-mkb"),
|
||||||
items: [!1, !1]
|
items: []
|
||||||
}];
|
}];
|
||||||
TAB_SHORTCUTS_ITEMS = [{
|
TAB_SHORTCUTS_ITEMS = [{
|
||||||
requiredVariants: "full",
|
requiredVariants: "full",
|
||||||
@ -3796,13 +3809,16 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
let prefDefinition = null;
|
let prefDefinition = null;
|
||||||
if (pref) prefDefinition = getPrefDefinition(pref);
|
if (pref) prefDefinition = getPrefDefinition(pref);
|
||||||
if (prefDefinition && !this.isSupportedVariant(prefDefinition.requiredVariants)) return;
|
if (prefDefinition && !this.isSupportedVariant(prefDefinition.requiredVariants)) return;
|
||||||
let label = prefDefinition?.label || setting.label, note = prefDefinition?.note || setting.note;
|
let label = prefDefinition?.label || setting.label, note = prefDefinition?.note || setting.note, unsupportedNote = prefDefinition?.unsupportedNote || setting.unsupportedNote;
|
||||||
const experimental = prefDefinition?.experimental || setting.experimental;
|
const experimental = prefDefinition?.experimental || setting.experimental;
|
||||||
if (settingTabContent.label && setting.pref) {
|
if (settingTabContent.label && setting.pref) {
|
||||||
if (prefDefinition?.suggest) typeof prefDefinition.suggest.lowest !== "undefined" && (this.suggestedSettings.lowest[setting.pref] = prefDefinition.suggest.lowest), typeof prefDefinition.suggest.highest !== "undefined" && (this.suggestedSettings.highest[setting.pref] = prefDefinition.suggest.highest);
|
if (prefDefinition?.suggest) typeof prefDefinition.suggest.lowest !== "undefined" && (this.suggestedSettings.lowest[setting.pref] = prefDefinition.suggest.lowest), typeof prefDefinition.suggest.highest !== "undefined" && (this.suggestedSettings.highest[setting.pref] = prefDefinition.suggest.highest);
|
||||||
}
|
}
|
||||||
if (experimental) if (label = "🧪 " + label, !note) note = t("experimental");
|
if (experimental) if (label = "🧪 " + label, !note) note = t("experimental");
|
||||||
else note = `${t("experimental")}: ${note}`;
|
else note = `${t("experimental")}: ${note}`;
|
||||||
|
let $note;
|
||||||
|
if (unsupportedNote) $note = CE("div", { class: "bx-settings-dialog-note" }, unsupportedNote);
|
||||||
|
else if (note) $note = CE("div", { class: "bx-settings-dialog-note" }, note);
|
||||||
let $label;
|
let $label;
|
||||||
const $row = CE("label", {
|
const $row = CE("label", {
|
||||||
class: "bx-settings-row",
|
class: "bx-settings-row",
|
||||||
@ -3811,7 +3827,7 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
_nearby: {
|
_nearby: {
|
||||||
orientation: "horizontal"
|
orientation: "horizontal"
|
||||||
}
|
}
|
||||||
}, $label = CE("span", { class: "bx-settings-label" }, label, note ? CE("div", { class: "bx-settings-dialog-note" }, note) : prefDefinition?.unsupported && CE("div", { class: "bx-settings-dialog-note" }, t("browser-unsupported-feature"))), !prefDefinition?.unsupported && $control), $link = $label.querySelector("a");
|
}, $label = CE("span", { class: "bx-settings-label" }, label, $note), !prefDefinition?.unsupported && $control), $link = $label.querySelector("a");
|
||||||
if ($link) $link.classList.add("bx-focusable"), setNearby($label, {
|
if ($link) $link.classList.add("bx-focusable"), setNearby($label, {
|
||||||
focus: $link
|
focus: $link
|
||||||
});
|
});
|
||||||
@ -3903,10 +3919,8 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
}));
|
}));
|
||||||
$tabContent.appendChild($title);
|
$tabContent.appendChild($title);
|
||||||
}
|
}
|
||||||
if (settingTabContent.note) {
|
if (settingTabContent.unsupportedNote) {
|
||||||
let $note;
|
const $note = CE("b", { class: "bx-note-unsupported" }, settingTabContent.unsupportedNote);
|
||||||
if (typeof settingTabContent.note === "string") $note = CE("b", { class: "bx-note-unsupported" }, settingTabContent.note);
|
|
||||||
else $note = settingTabContent.note;
|
|
||||||
$tabContent.appendChild($note);
|
$tabContent.appendChild($note);
|
||||||
}
|
}
|
||||||
if (settingTabContent.unsupported) continue;
|
if (settingTabContent.unsupported) continue;
|
||||||
@ -5556,6 +5570,7 @@ function waitForRootDialog() {
|
|||||||
observer.observe(document.documentElement, { subtree: !0, childList: !0 });
|
observer.observe(document.documentElement, { subtree: !0, childList: !0 });
|
||||||
}
|
}
|
||||||
function main() {
|
function main() {
|
||||||
|
if (getPref("game_msfs2020_force_native_mkb")) BX_FLAGS.ForceNativeMkbTitles.push("9PMQDM08SNK9");
|
||||||
if (patchRtcPeerConnection(), patchRtcCodecs(), interceptHttpRequests(), patchVideoApi(), patchCanvasContext(), getPref("audio_enable_volume_control") && patchAudioContext(), getPref("block_tracking")) patchMeControl(), disableAdobeAudienceManager();
|
if (patchRtcPeerConnection(), patchRtcCodecs(), interceptHttpRequests(), patchVideoApi(), patchCanvasContext(), getPref("audio_enable_volume_control") && patchAudioContext(), getPref("block_tracking")) patchMeControl(), disableAdobeAudienceManager();
|
||||||
if (waitForRootDialog(), addCss(), Toast.setup(), GuideMenu.addEventListeners(), StreamStatsCollector.setupEvents(), StreamBadges.setupEvents(), StreamStats.setupEvents(), getPref("controller_show_connection_status")) window.addEventListener("gamepadconnected", (e) => showGamepadToast(e.gamepad)), window.addEventListener("gamepaddisconnected", (e) => showGamepadToast(e.gamepad));
|
if (waitForRootDialog(), addCss(), Toast.setup(), GuideMenu.addEventListeners(), StreamStatsCollector.setupEvents(), StreamBadges.setupEvents(), StreamStats.setupEvents(), getPref("controller_show_connection_status")) window.addEventListener("gamepadconnected", (e) => showGamepadToast(e.gamepad)), window.addEventListener("gamepaddisconnected", (e) => showGamepadToast(e.gamepad));
|
||||||
}
|
}
|
||||||
|
2
dist/better-xcloud.meta.js
vendored
2
dist/better-xcloud.meta.js
vendored
@ -1,5 +1,5 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Better xCloud
|
// @name Better xCloud
|
||||||
// @namespace https://github.com/redphx
|
// @namespace https://github.com/redphx
|
||||||
// @version 5.8.0
|
// @version 5.8.1
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
61
dist/better-xcloud.user.js
vendored
61
dist/better-xcloud.user.js
vendored
@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Better xCloud
|
// @name Better xCloud
|
||||||
// @namespace https://github.com/redphx
|
// @namespace https://github.com/redphx
|
||||||
// @version 5.8.0
|
// @version 5.8.1
|
||||||
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
||||||
// @author redphx
|
// @author redphx
|
||||||
// @license MIT
|
// @license MIT
|
||||||
@ -120,9 +120,9 @@ function deepClone(obj) {
|
|||||||
if (!obj) return {};
|
if (!obj) return {};
|
||||||
return JSON.parse(JSON.stringify(obj));
|
return JSON.parse(JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
var SCRIPT_VERSION = "5.8.0", SCRIPT_VARIANT = "full", AppInterface = window.AppInterface;
|
var SCRIPT_VERSION = "5.8.1", SCRIPT_VARIANT = "full", AppInterface = window.AppInterface;
|
||||||
UserAgent.init();
|
UserAgent.init();
|
||||||
var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, STATES = {
|
var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, supportMkb = AppInterface || !userAgent.match(/(android|iphone|ipad)/), STATES = {
|
||||||
supportedRegion: !0,
|
supportedRegion: !0,
|
||||||
serverRegions: {},
|
serverRegions: {},
|
||||||
selectedRegion: {},
|
selectedRegion: {},
|
||||||
@ -139,7 +139,8 @@ var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.inclu
|
|||||||
userAgent: {
|
userAgent: {
|
||||||
isTv,
|
isTv,
|
||||||
capabilities: {
|
capabilities: {
|
||||||
touch: userAgentHasTouchSupport
|
touch: userAgentHasTouchSupport,
|
||||||
|
mkb: supportMkb
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
currentStream: {},
|
currentStream: {},
|
||||||
@ -353,10 +354,11 @@ var SUPPORTED_LANGUAGES = {
|
|||||||
disabled: "Disabled",
|
disabled: "Disabled",
|
||||||
disconnected: "Disconnected",
|
disconnected: "Disconnected",
|
||||||
download: "Download",
|
download: "Download",
|
||||||
|
downloaded: "Downloaded",
|
||||||
edit: "Edit",
|
edit: "Edit",
|
||||||
"enable-controller-shortcuts": "Enable controller shortcuts",
|
"enable-controller-shortcuts": "Enable controller shortcuts",
|
||||||
"enable-local-co-op-support": "Enable local co-op support",
|
"enable-local-co-op-support": "Enable local co-op support",
|
||||||
"enable-local-co-op-support-note": "Only works if the game doesn't require a different profile",
|
"enable-local-co-op-support-note": "Only works with some games",
|
||||||
"enable-mic-on-startup": "Enable microphone on game launch",
|
"enable-mic-on-startup": "Enable microphone on game launch",
|
||||||
"enable-mkb": "Emulate controller with Mouse & Keyboard",
|
"enable-mkb": "Emulate controller with Mouse & Keyboard",
|
||||||
"enable-quick-glance-mode": "Enable \"Quick Glance\" mode",
|
"enable-quick-glance-mode": "Enable \"Quick Glance\" mode",
|
||||||
@ -366,7 +368,7 @@ var SUPPORTED_LANGUAGES = {
|
|||||||
experimental: "Experimental",
|
experimental: "Experimental",
|
||||||
export: "Export",
|
export: "Export",
|
||||||
fast: "Fast",
|
fast: "Fast",
|
||||||
"fortnite-allow-stw-mode": "Allows playing STW mode on mobile",
|
"fortnite-allow-stw-mode": "Allows playing \"Save the World\" mode on mobile",
|
||||||
"fortnite-force-console-version": "Fortnite: force console version",
|
"fortnite-force-console-version": "Fortnite: force console version",
|
||||||
"game-bar": "Game Bar",
|
"game-bar": "Game Bar",
|
||||||
"getting-consoles-list": "Getting the list of consoles...",
|
"getting-consoles-list": "Getting the list of consoles...",
|
||||||
@ -409,6 +411,7 @@ var SUPPORTED_LANGUAGES = {
|
|||||||
"mkb-disclaimer": "Using this feature when playing online could be viewed as cheating",
|
"mkb-disclaimer": "Using this feature when playing online could be viewed as cheating",
|
||||||
"mouse-and-keyboard": "Mouse & Keyboard",
|
"mouse-and-keyboard": "Mouse & Keyboard",
|
||||||
"mouse-wheel": "Mouse wheel",
|
"mouse-wheel": "Mouse wheel",
|
||||||
|
"msfs2020-force-native-mkb": "MSFS2020: force native M&KB support",
|
||||||
muted: "Muted",
|
muted: "Muted",
|
||||||
name: "Name",
|
name: "Name",
|
||||||
"native-mkb": "Native Mouse & Keyboard",
|
"native-mkb": "Native Mouse & Keyboard",
|
||||||
@ -608,6 +611,7 @@ var SUPPORTED_LANGUAGES = {
|
|||||||
unmuted: "Unmuted",
|
unmuted: "Unmuted",
|
||||||
"unsharp-masking": "Unsharp masking",
|
"unsharp-masking": "Unsharp masking",
|
||||||
upload: "Upload",
|
upload: "Upload",
|
||||||
|
uploaded: "Uploaded",
|
||||||
"use-mouse-absolute-position": "Use mouse's absolute position",
|
"use-mouse-absolute-position": "Use mouse's absolute position",
|
||||||
"use-this-at-your-own-risk": "Use this at your own risk",
|
"use-this-at-your-own-risk": "Use this at your own risk",
|
||||||
"user-agent-profile": "User-Agent profile",
|
"user-agent-profile": "User-Agent profile",
|
||||||
@ -1219,7 +1223,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
|
|||||||
options: getSupportedCodecProfiles(),
|
options: getSupportedCodecProfiles(),
|
||||||
ready: (setting) => {
|
ready: (setting) => {
|
||||||
const options = setting.options, keys = Object.keys(options);
|
const options = setting.options, keys = Object.keys(options);
|
||||||
if (keys.length <= 1) setting.unsupported = !0, setting.note = "⚠️ " + t("browser-unsupported-feature");
|
if (keys.length <= 1) setting.unsupported = !0, setting.unsupportedNote = "⚠️ " + t("browser-unsupported-feature");
|
||||||
setting.suggest = {
|
setting.suggest = {
|
||||||
lowest: keys.length === 1 ? keys[0] : keys[1],
|
lowest: keys.length === 1 ? keys[0] : keys[1],
|
||||||
highest: keys[keys.length - 1]
|
highest: keys[keys.length - 1]
|
||||||
@ -1399,15 +1403,12 @@ class GlobalSettingsStorage extends BaseSettingsStore {
|
|||||||
requiredVariants: "full",
|
requiredVariants: "full",
|
||||||
label: t("enable-mkb"),
|
label: t("enable-mkb"),
|
||||||
default: !1,
|
default: !1,
|
||||||
unsupported: (() => {
|
unsupported: !STATES.userAgent.capabilities.mkb,
|
||||||
const userAgent2 = (window.navigator.orgUserAgent || window.navigator.userAgent || "").toLowerCase();
|
|
||||||
return !AppInterface && userAgent2.match(/(android|iphone|ipad)/) ? t("browser-unsupported-feature") : !1;
|
|
||||||
})(),
|
|
||||||
ready: (setting) => {
|
ready: (setting) => {
|
||||||
let note, url;
|
let note, url;
|
||||||
if (setting.unsupported) note = t("browser-unsupported-feature"), url = "https://github.com/redphx/better-xcloud/issues/206#issuecomment-1920475657";
|
if (setting.unsupported) note = t("browser-unsupported-feature"), url = "https://github.com/redphx/better-xcloud/issues/206#issuecomment-1920475657";
|
||||||
else note = t("mkb-disclaimer"), url = "https://better-xcloud.github.io/mouse-and-keyboard/#disclaimer";
|
else note = t("mkb-disclaimer"), url = "https://better-xcloud.github.io/mouse-and-keyboard/#disclaimer";
|
||||||
setting.note = CE("a", {
|
setting.unsupportedNote = CE("a", {
|
||||||
href: url,
|
href: url,
|
||||||
target: "_blank"
|
target: "_blank"
|
||||||
}, "⚠️ " + note);
|
}, "⚠️ " + note);
|
||||||
@ -1689,8 +1690,8 @@ class GlobalSettingsStorage extends BaseSettingsStore {
|
|||||||
dt: `${"dt".toUpperCase()}: ${t("stat-decode-time")}`,
|
dt: `${"dt".toUpperCase()}: ${t("stat-decode-time")}`,
|
||||||
pl: `${"pl".toUpperCase()}: ${t("stat-packets-lost")}`,
|
pl: `${"pl".toUpperCase()}: ${t("stat-packets-lost")}`,
|
||||||
fl: `${"fl".toUpperCase()}: ${t("stat-frames-lost")}`,
|
fl: `${"fl".toUpperCase()}: ${t("stat-frames-lost")}`,
|
||||||
dl: `${"dl".toUpperCase()}: ${t("download")}`,
|
dl: `${"dl".toUpperCase()}: ${t("downloaded")}`,
|
||||||
ul: `${"ul".toUpperCase()}: ${t("upload")}`
|
ul: `${"ul".toUpperCase()}: ${t("uploaded")}`
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
size: 6
|
size: 6
|
||||||
@ -1764,6 +1765,12 @@ class GlobalSettingsStorage extends BaseSettingsStore {
|
|||||||
label: "🎮 " + t("fortnite-force-console-version"),
|
label: "🎮 " + t("fortnite-force-console-version"),
|
||||||
default: !1,
|
default: !1,
|
||||||
note: t("fortnite-allow-stw-mode")
|
note: t("fortnite-allow-stw-mode")
|
||||||
|
},
|
||||||
|
game_msfs2020_force_native_mkb: {
|
||||||
|
requiredVariants: "full",
|
||||||
|
label: "✈️ " + t("msfs2020-force-native-mkb"),
|
||||||
|
default: !1,
|
||||||
|
note: t("may-not-work-properly")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -1895,11 +1902,11 @@ class StreamStats {
|
|||||||
$element: CE("span")
|
$element: CE("span")
|
||||||
},
|
},
|
||||||
dl: {
|
dl: {
|
||||||
name: t("download"),
|
name: t("downloaded"),
|
||||||
$element: CE("span")
|
$element: CE("span")
|
||||||
},
|
},
|
||||||
ul: {
|
ul: {
|
||||||
name: t("upload"),
|
name: t("uploaded"),
|
||||||
$element: CE("span")
|
$element: CE("span")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -4643,8 +4650,14 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
requiredVariants: "full",
|
requiredVariants: "full",
|
||||||
group: "mkb",
|
group: "mkb",
|
||||||
label: t("mouse-and-keyboard"),
|
label: t("mouse-and-keyboard"),
|
||||||
|
unsupportedNote: !STATES.userAgent.capabilities.mkb ? CE("a", {
|
||||||
|
href: "https://github.com/redphx/better-xcloud/issues/206#issuecomment-1920475657",
|
||||||
|
target: "_blank"
|
||||||
|
}, "⚠️ " + t("browser-unsupported-feature")) : null,
|
||||||
|
unsupported: !STATES.userAgent.capabilities.mkb,
|
||||||
items: [
|
items: [
|
||||||
"native_mkb_enabled",
|
"native_mkb_enabled",
|
||||||
|
"game_msfs2020_force_native_mkb",
|
||||||
"mkb_enabled",
|
"mkb_enabled",
|
||||||
"mkb_hide_idle_cursor"
|
"mkb_hide_idle_cursor"
|
||||||
]
|
]
|
||||||
@ -4652,8 +4665,8 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
requiredVariants: "full",
|
requiredVariants: "full",
|
||||||
group: "touch-control",
|
group: "touch-control",
|
||||||
label: t("touch-controller"),
|
label: t("touch-controller"),
|
||||||
note: !STATES.userAgent.capabilities.touch ? "⚠️ " + t("device-unsupported-touch") : null,
|
|
||||||
unsupported: !STATES.userAgent.capabilities.touch,
|
unsupported: !STATES.userAgent.capabilities.touch,
|
||||||
|
unsupportedNote: !STATES.userAgent.capabilities.touch ? "⚠️ " + t("device-unsupported-touch") : null,
|
||||||
items: [
|
items: [
|
||||||
"stream_touch_controller",
|
"stream_touch_controller",
|
||||||
"stream_touch_controller_auto_off",
|
"stream_touch_controller_auto_off",
|
||||||
@ -5229,13 +5242,16 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
let prefDefinition = null;
|
let prefDefinition = null;
|
||||||
if (pref) prefDefinition = getPrefDefinition(pref);
|
if (pref) prefDefinition = getPrefDefinition(pref);
|
||||||
if (prefDefinition && !this.isSupportedVariant(prefDefinition.requiredVariants)) return;
|
if (prefDefinition && !this.isSupportedVariant(prefDefinition.requiredVariants)) return;
|
||||||
let label = prefDefinition?.label || setting.label, note = prefDefinition?.note || setting.note;
|
let label = prefDefinition?.label || setting.label, note = prefDefinition?.note || setting.note, unsupportedNote = prefDefinition?.unsupportedNote || setting.unsupportedNote;
|
||||||
const experimental = prefDefinition?.experimental || setting.experimental;
|
const experimental = prefDefinition?.experimental || setting.experimental;
|
||||||
if (settingTabContent.label && setting.pref) {
|
if (settingTabContent.label && setting.pref) {
|
||||||
if (prefDefinition?.suggest) typeof prefDefinition.suggest.lowest !== "undefined" && (this.suggestedSettings.lowest[setting.pref] = prefDefinition.suggest.lowest), typeof prefDefinition.suggest.highest !== "undefined" && (this.suggestedSettings.highest[setting.pref] = prefDefinition.suggest.highest);
|
if (prefDefinition?.suggest) typeof prefDefinition.suggest.lowest !== "undefined" && (this.suggestedSettings.lowest[setting.pref] = prefDefinition.suggest.lowest), typeof prefDefinition.suggest.highest !== "undefined" && (this.suggestedSettings.highest[setting.pref] = prefDefinition.suggest.highest);
|
||||||
}
|
}
|
||||||
if (experimental) if (label = "🧪 " + label, !note) note = t("experimental");
|
if (experimental) if (label = "🧪 " + label, !note) note = t("experimental");
|
||||||
else note = `${t("experimental")}: ${note}`;
|
else note = `${t("experimental")}: ${note}`;
|
||||||
|
let $note;
|
||||||
|
if (unsupportedNote) $note = CE("div", { class: "bx-settings-dialog-note" }, unsupportedNote);
|
||||||
|
else if (note) $note = CE("div", { class: "bx-settings-dialog-note" }, note);
|
||||||
let $label;
|
let $label;
|
||||||
const $row = CE("label", {
|
const $row = CE("label", {
|
||||||
class: "bx-settings-row",
|
class: "bx-settings-row",
|
||||||
@ -5244,7 +5260,7 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
_nearby: {
|
_nearby: {
|
||||||
orientation: "horizontal"
|
orientation: "horizontal"
|
||||||
}
|
}
|
||||||
}, $label = CE("span", { class: "bx-settings-label" }, label, note ? CE("div", { class: "bx-settings-dialog-note" }, note) : prefDefinition?.unsupported && CE("div", { class: "bx-settings-dialog-note" }, t("browser-unsupported-feature"))), !prefDefinition?.unsupported && $control), $link = $label.querySelector("a");
|
}, $label = CE("span", { class: "bx-settings-label" }, label, $note), !prefDefinition?.unsupported && $control), $link = $label.querySelector("a");
|
||||||
if ($link) $link.classList.add("bx-focusable"), setNearby($label, {
|
if ($link) $link.classList.add("bx-focusable"), setNearby($label, {
|
||||||
focus: $link
|
focus: $link
|
||||||
});
|
});
|
||||||
@ -5336,10 +5352,8 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
}));
|
}));
|
||||||
$tabContent.appendChild($title);
|
$tabContent.appendChild($title);
|
||||||
}
|
}
|
||||||
if (settingTabContent.note) {
|
if (settingTabContent.unsupportedNote) {
|
||||||
let $note;
|
const $note = CE("b", { class: "bx-note-unsupported" }, settingTabContent.unsupportedNote);
|
||||||
if (typeof settingTabContent.note === "string") $note = CE("b", { class: "bx-note-unsupported" }, settingTabContent.note);
|
|
||||||
else $note = settingTabContent.note;
|
|
||||||
$tabContent.appendChild($note);
|
$tabContent.appendChild($note);
|
||||||
}
|
}
|
||||||
if (settingTabContent.unsupported) continue;
|
if (settingTabContent.unsupported) continue;
|
||||||
@ -7781,6 +7795,7 @@ function waitForRootDialog() {
|
|||||||
observer.observe(document.documentElement, { subtree: !0, childList: !0 });
|
observer.observe(document.documentElement, { subtree: !0, childList: !0 });
|
||||||
}
|
}
|
||||||
function main() {
|
function main() {
|
||||||
|
if (getPref("game_msfs2020_force_native_mkb")) BX_FLAGS.ForceNativeMkbTitles.push("9PMQDM08SNK9");
|
||||||
if (patchRtcPeerConnection(), patchRtcCodecs(), interceptHttpRequests(), patchVideoApi(), patchCanvasContext(), AppInterface && patchPointerLockApi(), getPref("audio_enable_volume_control") && patchAudioContext(), getPref("block_tracking")) patchMeControl(), disableAdobeAudienceManager();
|
if (patchRtcPeerConnection(), patchRtcCodecs(), interceptHttpRequests(), patchVideoApi(), patchCanvasContext(), AppInterface && patchPointerLockApi(), getPref("audio_enable_volume_control") && patchAudioContext(), getPref("block_tracking")) patchMeControl(), disableAdobeAudienceManager();
|
||||||
if (waitForRootDialog(), addCss(), Toast.setup(), GuideMenu.addEventListeners(), StreamStatsCollector.setupEvents(), StreamBadges.setupEvents(), StreamStats.setupEvents(), getPref("game_bar_position") !== "off" && GameBar.getInstance(), Screenshot.setup(), STATES.userAgent.capabilities.touch && TouchController.updateCustomList(), overridePreloadState(), VibrationManager.initialSetup(), BX_FLAGS.CheckForUpdate && checkForUpdate(), Patcher.init(), disablePwa(), getPref("xhome_enabled")) RemotePlayManager.detect();
|
if (waitForRootDialog(), addCss(), Toast.setup(), GuideMenu.addEventListeners(), StreamStatsCollector.setupEvents(), StreamBadges.setupEvents(), StreamStats.setupEvents(), getPref("game_bar_position") !== "off" && GameBar.getInstance(), Screenshot.setup(), STATES.userAgent.capabilities.touch && TouchController.updateCustomList(), overridePreloadState(), VibrationManager.initialSetup(), BX_FLAGS.CheckForUpdate && checkForUpdate(), Patcher.init(), disablePwa(), getPref("xhome_enabled")) RemotePlayManager.detect();
|
||||||
if (getPref("stream_touch_controller") === "all") TouchController.setup();
|
if (getPref("stream_touch_controller") === "all") TouchController.setup();
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.1.10",
|
"@types/bun": "^1.1.10",
|
||||||
"@types/node": "^22.7.4",
|
"@types/node": "^22.7.5",
|
||||||
"@types/stylus": "^0.48.43",
|
"@types/stylus": "^0.48.43",
|
||||||
"eslint": "^9.12.0",
|
"eslint": "^9.12.0",
|
||||||
"eslint-plugin-compat": "^6.0.1",
|
"eslint-plugin-compat": "^6.0.1",
|
||||||
|
@ -98,4 +98,5 @@ export enum PrefKey {
|
|||||||
REMOTE_PLAY_RESOLUTION = 'xhome_resolution',
|
REMOTE_PLAY_RESOLUTION = 'xhome_resolution',
|
||||||
|
|
||||||
GAME_FORTNITE_FORCE_CONSOLE = 'game_fortnite_force_console',
|
GAME_FORTNITE_FORCE_CONSOLE = 'game_fortnite_force_console',
|
||||||
|
GAME_MSFS2020_FORCE_NATIVE_MKB = 'game_msfs2020_force_native_mkb',
|
||||||
}
|
}
|
||||||
|
@ -378,6 +378,10 @@ function waitForRootDialog() {
|
|||||||
|
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
|
if (getPref(PrefKey.GAME_MSFS2020_FORCE_NATIVE_MKB)) {
|
||||||
|
BX_FLAGS.ForceNativeMkbTitles.push('9PMQDM08SNK9');
|
||||||
|
}
|
||||||
|
|
||||||
// Monkey patches
|
// Monkey patches
|
||||||
patchRtcPeerConnection();
|
patchRtcPeerConnection();
|
||||||
patchRtcCodecs();
|
patchRtcCodecs();
|
||||||
|
@ -58,11 +58,11 @@ export class StreamStats {
|
|||||||
$element: CE('span'),
|
$element: CE('span'),
|
||||||
},
|
},
|
||||||
[StreamStat.DOWNLOAD]: {
|
[StreamStat.DOWNLOAD]: {
|
||||||
name: t('download'),
|
name: t('downloaded'),
|
||||||
$element: CE('span'),
|
$element: CE('span'),
|
||||||
},
|
},
|
||||||
[StreamStat.UPLOAD]: {
|
[StreamStat.UPLOAD]: {
|
||||||
name: t('upload'),
|
name: t('uploaded'),
|
||||||
$element: CE('span'),
|
$element: CE('span'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -37,6 +37,7 @@ type SettingTabContentItem = Partial<{
|
|||||||
content: HTMLElement | (() => HTMLElement);
|
content: HTMLElement | (() => HTMLElement);
|
||||||
options: {[key: string]: string};
|
options: {[key: string]: string};
|
||||||
unsupported: boolean;
|
unsupported: boolean;
|
||||||
|
unsupportedNote: string;
|
||||||
onChange: (e: any, value: number) => void;
|
onChange: (e: any, value: number) => void;
|
||||||
onCreated: (setting: SettingTabContentItem, $control: any) => void;
|
onCreated: (setting: SettingTabContentItem, $control: any) => void;
|
||||||
params: any;
|
params: any;
|
||||||
@ -46,8 +47,8 @@ type SettingTabContentItem = Partial<{
|
|||||||
type SettingTabContent = {
|
type SettingTabContent = {
|
||||||
group: 'general' | 'server' | 'stream' | 'game-bar' | 'co-op' | 'mkb' | 'touch-control' | 'loading-screen' | 'ui' | 'other' | 'advanced' | 'footer' | 'audio' | 'video' | 'controller' | 'native-mkb' | 'stats' | 'controller-shortcuts';
|
group: 'general' | 'server' | 'stream' | 'game-bar' | 'co-op' | 'mkb' | 'touch-control' | 'loading-screen' | 'ui' | 'other' | 'advanced' | 'footer' | 'audio' | 'video' | 'controller' | 'native-mkb' | 'stats' | 'controller-shortcuts';
|
||||||
label?: string;
|
label?: string;
|
||||||
note?: string | Text | null;
|
|
||||||
unsupported?: boolean;
|
unsupported?: boolean;
|
||||||
|
unsupportedNote?: string | Text | null;
|
||||||
helpUrl?: string;
|
helpUrl?: string;
|
||||||
content?: any;
|
content?: any;
|
||||||
items?: Array<SettingTabContentItem | PrefKey | (($parent: HTMLElement) => void) | false>;
|
items?: Array<SettingTabContentItem | PrefKey | (($parent: HTMLElement) => void) | false>;
|
||||||
@ -220,8 +221,14 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
requiredVariants: 'full',
|
requiredVariants: 'full',
|
||||||
group: 'mkb',
|
group: 'mkb',
|
||||||
label: t('mouse-and-keyboard'),
|
label: t('mouse-and-keyboard'),
|
||||||
|
unsupportedNote: !STATES.userAgent.capabilities.mkb ? CE('a', {
|
||||||
|
href: 'https://github.com/redphx/better-xcloud/issues/206#issuecomment-1920475657',
|
||||||
|
target: '_blank',
|
||||||
|
}, '⚠️ ' + t('browser-unsupported-feature')) : null,
|
||||||
|
unsupported: !STATES.userAgent.capabilities.mkb,
|
||||||
items: [
|
items: [
|
||||||
PrefKey.NATIVE_MKB_ENABLED,
|
PrefKey.NATIVE_MKB_ENABLED,
|
||||||
|
PrefKey.GAME_MSFS2020_FORCE_NATIVE_MKB,
|
||||||
PrefKey.MKB_ENABLED,
|
PrefKey.MKB_ENABLED,
|
||||||
PrefKey.MKB_HIDE_IDLE_CURSOR,
|
PrefKey.MKB_HIDE_IDLE_CURSOR,
|
||||||
],
|
],
|
||||||
@ -229,8 +236,8 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
requiredVariants: 'full',
|
requiredVariants: 'full',
|
||||||
group: 'touch-control',
|
group: 'touch-control',
|
||||||
label: t('touch-controller'),
|
label: t('touch-controller'),
|
||||||
note: !STATES.userAgent.capabilities.touch ? '⚠️ ' + t('device-unsupported-touch') : null,
|
|
||||||
unsupported: !STATES.userAgent.capabilities.touch,
|
unsupported: !STATES.userAgent.capabilities.touch,
|
||||||
|
unsupportedNote: !STATES.userAgent.capabilities.touch ? '⚠️ ' + t('device-unsupported-touch') : null,
|
||||||
items: [
|
items: [
|
||||||
PrefKey.STREAM_TOUCH_CONTROLLER,
|
PrefKey.STREAM_TOUCH_CONTROLLER,
|
||||||
PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF,
|
PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF,
|
||||||
@ -516,17 +523,17 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
requiredVariants: 'full',
|
requiredVariants: 'full',
|
||||||
group: 'native-mkb',
|
group: 'native-mkb',
|
||||||
label: t('native-mkb'),
|
label: t('native-mkb'),
|
||||||
items: [isFullVersion() && {
|
items: isFullVersion() ? [{
|
||||||
pref: PrefKey.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY,
|
pref: PrefKey.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY,
|
||||||
onChange: (e: any, value: number) => {
|
onChange: (e: any, value: number) => {
|
||||||
NativeMkbHandler.getInstance().setVerticalScrollMultiplier(value / 100);
|
NativeMkbHandler.getInstance().setVerticalScrollMultiplier(value / 100);
|
||||||
},
|
},
|
||||||
}, isFullVersion() && {
|
}, {
|
||||||
pref: PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY,
|
pref: PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY,
|
||||||
onChange: (e: any, value: number) => {
|
onChange: (e: any, value: number) => {
|
||||||
NativeMkbHandler.getInstance().setHorizontalScrollMultiplier(value / 100);
|
NativeMkbHandler.getInstance().setHorizontalScrollMultiplier(value / 100);
|
||||||
},
|
},
|
||||||
}],
|
}] : [],
|
||||||
}];
|
}];
|
||||||
|
|
||||||
private readonly TAB_SHORTCUTS_ITEMS: Array<SettingTabContent | false> = [{
|
private readonly TAB_SHORTCUTS_ITEMS: Array<SettingTabContent | false> = [{
|
||||||
@ -1132,6 +1139,7 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
|
|
||||||
let label = prefDefinition?.label || setting.label;
|
let label = prefDefinition?.label || setting.label;
|
||||||
let note = prefDefinition?.note || setting.note;
|
let note = prefDefinition?.note || setting.note;
|
||||||
|
let unsupportedNote = prefDefinition?.unsupportedNote || setting.unsupportedNote;
|
||||||
const experimental = prefDefinition?.experimental || setting.experimental;
|
const experimental = prefDefinition?.experimental || setting.experimental;
|
||||||
|
|
||||||
if (settingTabContent.label && setting.pref) {
|
if (settingTabContent.label && setting.pref) {
|
||||||
@ -1151,6 +1159,13 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let $note;
|
||||||
|
if (unsupportedNote) {
|
||||||
|
$note = CE('div', {class: 'bx-settings-dialog-note'}, unsupportedNote);
|
||||||
|
} else if (note) {
|
||||||
|
$note = CE('div', {class: 'bx-settings-dialog-note'}, note);
|
||||||
|
}
|
||||||
|
|
||||||
let $label;
|
let $label;
|
||||||
|
|
||||||
const $row = CE('label', {
|
const $row = CE('label', {
|
||||||
@ -1163,7 +1178,7 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
},
|
},
|
||||||
$label = CE('span', {class: 'bx-settings-label'},
|
$label = CE('span', {class: 'bx-settings-label'},
|
||||||
label,
|
label,
|
||||||
note ? CE('div', {class: 'bx-settings-dialog-note'}, note) : prefDefinition?.unsupported && CE('div', {class: 'bx-settings-dialog-note'}, t('browser-unsupported-feature')),
|
$note,
|
||||||
),
|
),
|
||||||
!prefDefinition?.unsupported && $control,
|
!prefDefinition?.unsupported && $control,
|
||||||
);
|
);
|
||||||
@ -1334,13 +1349,8 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add note
|
// Add note
|
||||||
if (settingTabContent.note) {
|
if (settingTabContent.unsupportedNote) {
|
||||||
let $note;
|
const $note = CE('b', {class: 'bx-note-unsupported'}, settingTabContent.unsupportedNote);
|
||||||
if (typeof settingTabContent.note === 'string') {
|
|
||||||
$note = CE('b', {class: 'bx-note-unsupported'}, settingTabContent.note);
|
|
||||||
} else {
|
|
||||||
$note = settingTabContent.note;
|
|
||||||
}
|
|
||||||
|
|
||||||
$tabContent.appendChild($note);
|
$tabContent.appendChild($note);
|
||||||
}
|
}
|
||||||
|
1
src/types/index.d.ts
vendored
1
src/types/index.d.ts
vendored
@ -46,6 +46,7 @@ type BxStates = {
|
|||||||
isTv: boolean;
|
isTv: boolean;
|
||||||
capabilities: {
|
capabilities: {
|
||||||
touch: boolean;
|
touch: boolean;
|
||||||
|
mkb: boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
3
src/types/preferences.d.ts
vendored
3
src/types/preferences.d.ts
vendored
@ -3,7 +3,8 @@ export type PreferenceSetting = {
|
|||||||
optionsGroup?: string;
|
optionsGroup?: string;
|
||||||
options?: {[index: string]: string};
|
options?: {[index: string]: string};
|
||||||
multipleOptions?: {[index: string]: string};
|
multipleOptions?: {[index: string]: string};
|
||||||
unsupported?: string | boolean;
|
unsupported?: boolean;
|
||||||
|
unsupported_note?: string | HTMLElement;
|
||||||
note?: string | HTMLElement;
|
note?: string | HTMLElement;
|
||||||
type?: SettingElementType;
|
type?: SettingElementType;
|
||||||
ready?: (setting: PreferenceSetting) => void;
|
ready?: (setting: PreferenceSetting) => void;
|
||||||
|
3
src/types/setting-definition.d.ts
vendored
3
src/types/setting-definition.d.ts
vendored
@ -20,7 +20,8 @@ export type SettingDefinition = {
|
|||||||
label: string;
|
label: string;
|
||||||
note: string | HTMLElement;
|
note: string | HTMLElement;
|
||||||
experimental: boolean;
|
experimental: boolean;
|
||||||
unsupported: string | boolean;
|
unsupported: boolean;
|
||||||
|
unsupportedNote: string | HTMLElement;
|
||||||
suggest: PartialRecord<SuggestedSettingCategory, any>,
|
suggest: PartialRecord<SuggestedSettingCategory, any>,
|
||||||
ready: (setting: SettingDefinition) => void;
|
ready: (setting: SettingDefinition) => void;
|
||||||
type: SettingElementType,
|
type: SettingElementType,
|
||||||
|
@ -13,6 +13,7 @@ const isTv = userAgent.includes('smart-tv') || userAgent.includes('smarttv') ||
|
|||||||
const isVr = window.navigator.userAgent.includes('VR') && window.navigator.userAgent.includes('OculusBrowser');
|
const isVr = window.navigator.userAgent.includes('VR') && window.navigator.userAgent.includes('OculusBrowser');
|
||||||
const browserHasTouchSupport = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
const browserHasTouchSupport = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||||||
const userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport;
|
const userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport;
|
||||||
|
const supportMkb = AppInterface || !userAgent.match(/(android|iphone|ipad)/);
|
||||||
|
|
||||||
export const STATES: BxStates = {
|
export const STATES: BxStates = {
|
||||||
supportedRegion: true,
|
supportedRegion: true,
|
||||||
@ -35,6 +36,7 @@ export const STATES: BxStates = {
|
|||||||
isTv: isTv,
|
isTv: isTv,
|
||||||
capabilities: {
|
capabilities: {
|
||||||
touch: userAgentHasTouchSupport,
|
touch: userAgentHasTouchSupport,
|
||||||
|
mkb: supportMkb,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
|
|
||||||
if (keys.length <= 1) { // Unsupported
|
if (keys.length <= 1) { // Unsupported
|
||||||
setting.unsupported = true;
|
setting.unsupported = true;
|
||||||
setting.note = '⚠️ ' + t('browser-unsupported-feature');
|
setting.unsupportedNote = '⚠️ ' + t('browser-unsupported-feature');
|
||||||
}
|
}
|
||||||
|
|
||||||
setting.suggest = {
|
setting.suggest = {
|
||||||
@ -393,10 +393,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
requiredVariants: 'full',
|
requiredVariants: 'full',
|
||||||
label: t('enable-mkb'),
|
label: t('enable-mkb'),
|
||||||
default: false,
|
default: false,
|
||||||
unsupported: ((): string | boolean => {
|
unsupported: !STATES.userAgent.capabilities.mkb,
|
||||||
const userAgent = ((window.navigator as any).orgUserAgent || window.navigator.userAgent || '').toLowerCase();
|
|
||||||
return !AppInterface && userAgent.match(/(android|iphone|ipad)/) ? t('browser-unsupported-feature') : false;
|
|
||||||
})(),
|
|
||||||
ready: (setting: SettingDefinition) => {
|
ready: (setting: SettingDefinition) => {
|
||||||
let note;
|
let note;
|
||||||
let url;
|
let url;
|
||||||
@ -408,7 +405,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
url = 'https://better-xcloud.github.io/mouse-and-keyboard/#disclaimer';
|
url = 'https://better-xcloud.github.io/mouse-and-keyboard/#disclaimer';
|
||||||
}
|
}
|
||||||
|
|
||||||
setting.note = CE('a', {
|
setting.unsupportedNote = CE('a', {
|
||||||
href: url,
|
href: url,
|
||||||
target: '_blank',
|
target: '_blank',
|
||||||
}, '⚠️ ' + note);
|
}, '⚠️ ' + note);
|
||||||
@ -722,8 +719,8 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
[StreamStat.DECODE_TIME]: `${StreamStat.DECODE_TIME.toUpperCase()}: ${t('stat-decode-time')}`,
|
[StreamStat.DECODE_TIME]: `${StreamStat.DECODE_TIME.toUpperCase()}: ${t('stat-decode-time')}`,
|
||||||
[StreamStat.PACKETS_LOST]: `${StreamStat.PACKETS_LOST.toUpperCase()}: ${t('stat-packets-lost')}`,
|
[StreamStat.PACKETS_LOST]: `${StreamStat.PACKETS_LOST.toUpperCase()}: ${t('stat-packets-lost')}`,
|
||||||
[StreamStat.FRAMES_LOST]: `${StreamStat.FRAMES_LOST.toUpperCase()}: ${t('stat-frames-lost')}`,
|
[StreamStat.FRAMES_LOST]: `${StreamStat.FRAMES_LOST.toUpperCase()}: ${t('stat-frames-lost')}`,
|
||||||
[StreamStat.DOWNLOAD]: `${StreamStat.DOWNLOAD.toUpperCase()}: ${t('download')}`,
|
[StreamStat.DOWNLOAD]: `${StreamStat.DOWNLOAD.toUpperCase()}: ${t('downloaded')}`,
|
||||||
[StreamStat.UPLOAD]: `${StreamStat.UPLOAD.toUpperCase()}: ${t('upload')}`,
|
[StreamStat.UPLOAD]: `${StreamStat.UPLOAD.toUpperCase()}: ${t('uploaded')}`,
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
size: 6,
|
size: 6,
|
||||||
@ -804,6 +801,13 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
default: false,
|
default: false,
|
||||||
note: t('fortnite-allow-stw-mode'),
|
note: t('fortnite-allow-stw-mode'),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[PrefKey.GAME_MSFS2020_FORCE_NATIVE_MKB]: {
|
||||||
|
requiredVariants: 'full',
|
||||||
|
label: '✈️ ' + t('msfs2020-force-native-mkb'),
|
||||||
|
default: false,
|
||||||
|
note: t('may-not-work-properly'),
|
||||||
|
},
|
||||||
} satisfies SettingDefinitions;
|
} satisfies SettingDefinitions;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -93,10 +93,11 @@ const Texts = {
|
|||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
"disconnected": "Disconnected",
|
"disconnected": "Disconnected",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
|
"downloaded": "Downloaded",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"enable-controller-shortcuts": "Enable controller shortcuts",
|
"enable-controller-shortcuts": "Enable controller shortcuts",
|
||||||
"enable-local-co-op-support": "Enable local co-op support",
|
"enable-local-co-op-support": "Enable local co-op support",
|
||||||
"enable-local-co-op-support-note": "Only works if the game doesn't require a different profile",
|
"enable-local-co-op-support-note": "Only works with some games",
|
||||||
"enable-mic-on-startup": "Enable microphone on game launch",
|
"enable-mic-on-startup": "Enable microphone on game launch",
|
||||||
"enable-mkb": "Emulate controller with Mouse & Keyboard",
|
"enable-mkb": "Emulate controller with Mouse & Keyboard",
|
||||||
"enable-quick-glance-mode": "Enable \"Quick Glance\" mode",
|
"enable-quick-glance-mode": "Enable \"Quick Glance\" mode",
|
||||||
@ -106,7 +107,7 @@ const Texts = {
|
|||||||
"experimental": "Experimental",
|
"experimental": "Experimental",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
"fast": "Fast",
|
"fast": "Fast",
|
||||||
"fortnite-allow-stw-mode": "Allows playing STW mode on mobile",
|
"fortnite-allow-stw-mode": "Allows playing \"Save the World\" mode on mobile",
|
||||||
"fortnite-force-console-version": "Fortnite: force console version",
|
"fortnite-force-console-version": "Fortnite: force console version",
|
||||||
"game-bar": "Game Bar",
|
"game-bar": "Game Bar",
|
||||||
"getting-consoles-list": "Getting the list of consoles...",
|
"getting-consoles-list": "Getting the list of consoles...",
|
||||||
@ -149,6 +150,7 @@ const Texts = {
|
|||||||
"mkb-disclaimer": "Using this feature when playing online could be viewed as cheating",
|
"mkb-disclaimer": "Using this feature when playing online could be viewed as cheating",
|
||||||
"mouse-and-keyboard": "Mouse & Keyboard",
|
"mouse-and-keyboard": "Mouse & Keyboard",
|
||||||
"mouse-wheel": "Mouse wheel",
|
"mouse-wheel": "Mouse wheel",
|
||||||
|
"msfs2020-force-native-mkb": "MSFS2020: force native M&KB support",
|
||||||
"muted": "Muted",
|
"muted": "Muted",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"native-mkb": "Native Mouse & Keyboard",
|
"native-mkb": "Native Mouse & Keyboard",
|
||||||
@ -348,6 +350,7 @@ const Texts = {
|
|||||||
"unmuted": "Unmuted",
|
"unmuted": "Unmuted",
|
||||||
"unsharp-masking": "Unsharp masking",
|
"unsharp-masking": "Unsharp masking",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
|
"uploaded": "Uploaded",
|
||||||
"use-mouse-absolute-position": "Use mouse's absolute position",
|
"use-mouse-absolute-position": "Use mouse's absolute position",
|
||||||
"use-this-at-your-own-risk": "Use this at your own risk",
|
"use-this-at-your-own-risk": "Use this at your own risk",
|
||||||
"user-agent-profile": "User-Agent profile",
|
"user-agent-profile": "User-Agent profile",
|
||||||
|
Reference in New Issue
Block a user