From 18027ed1c56ecae92e52350eaaaade6113a67e27 Mon Sep 17 00:00:00 2001 From: redphx <96280+redphx@users.noreply.github.com> Date: Sat, 25 May 2024 09:52:25 +0700 Subject: [PATCH] Update better-xcloud.user.js --- dist/better-xcloud.user.js | 190 ++++++++++++++++++++++++------------- 1 file changed, 126 insertions(+), 64 deletions(-) diff --git a/dist/better-xcloud.user.js b/dist/better-xcloud.user.js index 20aeaf4..8522619 100644 --- a/dist/better-xcloud.user.js +++ b/dist/better-xcloud.user.js @@ -149,6 +149,7 @@ var BxEvent; BxEvent2["GAME_BAR_ACTION_ACTIVATED"] = "bx-game-bar-action-activated"; BxEvent2["MICROPHONE_STATE_CHANGED"] = "bx-microphone-state-changed"; BxEvent2["CAPTURE_SCREENSHOT"] = "bx-capture-screenshot"; + BxEvent2["GAINNODE_VOLUME_CHANGED"] = "bx-gainnode-volume-changed"; })(BxEvent || (BxEvent = {})); var XcloudEvent; (function(XcloudEvent2) { @@ -504,6 +505,8 @@ var Texts = { contrast: "Contrast", controller: "Controller", "controller-shortcuts": "Controller shortcuts", + "controller-shortcuts-connect-note": "Connect a controller to use this feature", + "controller-shortcuts-xbox-note": "The Xbox button is the one that opens the Guide menu", "controller-vibration": "Controller vibration", copy: "Copy", custom: "Custom", @@ -565,7 +568,6 @@ var Texts = { "mkb-click-to-activate": "Click to activate", "mkb-disclaimer": "Using this feature when playing online could be viewed as cheating", "mouse-and-keyboard": "Mouse & Keyboard", - "mute-unmute-sound": "Mute/unmute sound", muted: "Muted", name: "Name", new: "New", @@ -667,8 +669,6 @@ var Texts = { "tc-standard-layout-style": "Standard layout's button style", "text-size": "Text size", toggle: "Toggle", - "toggle-microphone": "Toggle microphone", - "toggle-stream-stats": "Toggle stream stats", "top-center": "Top-center", "top-left": "Top-left", "top-right": "Top-right", @@ -932,7 +932,7 @@ class SettingElement { $range.addEventListener("input", (e) => { value = parseInt(e.target.value); $text.textContent = renderTextValue(value); - onChange && onChange(e, value); + !e.ignoreOnChange && onChange && onChange(e, value); }); $wrapper.appendChild($range); if (options.ticks || options.exactTicks) { @@ -2270,6 +2270,7 @@ class Preferences { value = this.#validateValue(key, value); this.#prefs[key] = value; this.#updateStorage(); + return value; } #updateStorage() { this.#storage.setItem(this.#key, JSON.stringify(this.#prefs)); @@ -2340,7 +2341,7 @@ class Toast { Toast.#timeout && clearTimeout(Toast.#timeout); Toast.#timeout = window.setTimeout(Toast.#hide, Toast.#DURATION); const [msg, status, options] = Toast.#stack.shift(); - if (options.html) { + if (options && options.html) { Toast.#$msg.innerHTML = msg; } else { Toast.#$msg.textContent = msg; @@ -3167,13 +3168,22 @@ class MkbHandler { } // src/modules/shortcuts/shortcut-microphone.ts +var MicrophoneState; +(function(MicrophoneState2) { + MicrophoneState2["REQUESTED"] = "Requested"; + MicrophoneState2["ENABLED"] = "Enabled"; + MicrophoneState2["MUTED"] = "Muted"; + MicrophoneState2["NOT_ALLOWED"] = "NotAllowed"; + MicrophoneState2["NOT_FOUND"] = "NotFound"; +})(MicrophoneState || (MicrophoneState = {})); + class MicrophoneShortcut { static toggle(showToast = true) { if (!window.BX_EXPOSED.streamSession) { return false; } const state = window.BX_EXPOSED.streamSession._microphoneState; - const enableMic = state === "Enabled" ? false : true; + const enableMic = state === MicrophoneState.ENABLED ? false : true; try { window.BX_EXPOSED.streamSession.tryEnableChatAsync(enableMic); showToast && Toast.show(t("microphone"), t(enableMic ? "unmuted" : "muted"), { instant: true }); @@ -3192,6 +3202,96 @@ class StreamUiShortcut { } } +// src/utils/utils.ts +function checkForUpdate() { + const CHECK_INTERVAL_SECONDS = 7200; + const currentVersion = getPref(PrefKey.CURRENT_VERSION); + const lastCheck = getPref(PrefKey.LAST_UPDATE_CHECK); + const now = Math.round(+new Date / 1000); + if (currentVersion === SCRIPT_VERSION && now - lastCheck < CHECK_INTERVAL_SECONDS) { + return; + } + setPref(PrefKey.LAST_UPDATE_CHECK, now); + fetch("https://api.github.com/repos/redphx/better-xcloud/releases/latest").then((response) => response.json()).then((json) => { + setPref(PrefKey.LATEST_VERSION, json.tag_name.substring(1)); + setPref(PrefKey.CURRENT_VERSION, SCRIPT_VERSION); + }); + Translations.updateTranslations(true); +} +function disablePwa() { + const userAgent2 = (window.navigator.orgUserAgent || window.navigator.userAgent || "").toLowerCase(); + if (!userAgent2) { + return; + } + if (!!AppInterface || UserAgent.isSafari(true)) { + Object.defineProperty(window.navigator, "standalone", { + value: true + }); + } +} +function hashCode(str2) { + let hash = 0; + for (let i = 0, len = str2.length;i < len; i++) { + const chr = str2.charCodeAt(i); + hash = (hash << 5) - hash + chr; + hash |= 0; + } + return hash; +} +function renderString(str2, obj) { + return str2.replace(/\$\{.+?\}/g, (match) => { + const key = match.substring(2, match.length - 1); + if (key in obj) { + return obj[key]; + } + return match; + }); +} +function ceilToNearest(value, interval) { + return Math.ceil(value / interval) * interval; +} +function floorToNearest(value, interval) { + return Math.floor(value / interval) * interval; +} + +// src/modules/shortcuts/shortcut-sound.ts +class SoundShortcut { + static increaseGainNodeVolume(amount) { + SoundShortcut.#adjustGainNodeVolume(amount); + } + static decreaseGainNodeVolume(amount) { + SoundShortcut.#adjustGainNodeVolume(-1 * Math.abs(amount)); + } + static #adjustGainNodeVolume(amount) { + if (!getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL)) { + return 0; + } + const currentValue = getPref(PrefKey.AUDIO_VOLUME); + let nearestValue; + if (amount > 0) { + nearestValue = ceilToNearest(currentValue, amount); + } else { + nearestValue = floorToNearest(currentValue, -1 * amount); + } + let newValue; + if (currentValue !== nearestValue) { + newValue = nearestValue; + } else { + newValue = currentValue + amount; + } + newValue = setPref(PrefKey.AUDIO_VOLUME, newValue); + SoundShortcut.setGainNodeVolume(newValue); + Toast.show(`${t("stream")} ❯ ${t("volume")}`, newValue + "%", { instant: true }); + BxEvent.dispatch(window, BxEvent.GAINNODE_VOLUME_CHANGED, { + volume: newValue + }); + return newValue; + } + static setGainNodeVolume(value) { + STATES.currentStream.audioGainNode && (STATES.currentStream.audioGainNode.gain.value = value / 100); + } +} + // src/modules/controller-shortcut.ts var ShortcutAction; (function(ShortcutAction2) { @@ -3256,6 +3356,12 @@ class ControllerShortcut { case ShortcutAction.STREAM_MENU_TOGGLE: StreamUiShortcut.showHideStreamMenu(); break; + case ShortcutAction.STREAM_VOLUME_INC: + SoundShortcut.increaseGainNodeVolume(10); + break; + case ShortcutAction.STREAM_VOLUME_DEC: + SoundShortcut.decreaseGainNodeVolume(10); + break; } } static #updateAction(profile, button, action) { @@ -3349,7 +3455,9 @@ class ControllerShortcut { [ShortcutAction.STREAM_SCREENSHOT_CAPTURE]: [t("stream"), t("take-screenshot")], [ShortcutAction.STREAM_STATS_TOGGLE]: [t("stream"), t("stats"), t("show-hide")], [ShortcutAction.STREAM_MICROPHONE_TOGGLE]: [t("stream"), t("microphone"), t("toggle")], - [ShortcutAction.STREAM_MENU_TOGGLE]: [t("stream"), t("menu"), t("show")] + [ShortcutAction.STREAM_MENU_TOGGLE]: [t("stream"), t("menu"), t("show")], + [ShortcutAction.STREAM_VOLUME_INC]: getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && [t("stream"), t("volume"), t("increase")], + [ShortcutAction.STREAM_VOLUME_DEC]: getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && [t("stream"), t("volume"), t("decrease")] } }; const $baseSelect = CE("select", { autocomplete: "off" }, CE("option", { value: "" }, "---")); @@ -3365,7 +3473,7 @@ class ControllerShortcut { continue; } if (Array.isArray(label)) { - label = label.join(" > "); + label = label.join(" ❯ "); } const $option = CE("option", { value: action }, label); $optGroup.appendChild($option); @@ -3705,15 +3813,6 @@ class TouchControlAction extends BaseGameBarAction { } // src/modules/game-bar/action-microphone.ts -var MicrophoneState; -(function(MicrophoneState2) { - MicrophoneState2["REQUESTED"] = "Requested"; - MicrophoneState2["ENABLED"] = "Enabled"; - MicrophoneState2["MUTED"] = "Muted"; - MicrophoneState2["NOT_ALLOWED"] = "NotAllowed"; - MicrophoneState2["NOT_FOUND"] = "NotFound"; -})(MicrophoneState || (MicrophoneState = {})); - class MicrophoneAction extends BaseGameBarAction { $content; visible = false; @@ -4680,10 +4779,19 @@ var setupQuickSettingsBar = function() { pref: PrefKey.AUDIO_VOLUME, label: t("volume"), onChange: (e, value) => { - STATES.currentStream.audioGainNode && (STATES.currentStream.audioGainNode.gain.value = value / 100); + SoundShortcut.setGainNodeVolume(value); }, params: { disabled: !getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) + }, + onMounted: ($elm) => { + const $range = $elm.querySelector("input[type=range"); + window.addEventListener(BxEvent.GAINNODE_VOLUME_CHANGED, (e) => { + $range.value = e.volume; + BxEvent.dispatch($range, "input", { + ignoreOnChange: true + }); + }); } } ] @@ -7135,52 +7243,6 @@ class MouseCursorHider { } } -// src/utils/utils.ts -function checkForUpdate() { - const CHECK_INTERVAL_SECONDS = 7200; - const currentVersion = getPref(PrefKey.CURRENT_VERSION); - const lastCheck = getPref(PrefKey.LAST_UPDATE_CHECK); - const now = Math.round(+new Date / 1000); - if (currentVersion === SCRIPT_VERSION && now - lastCheck < CHECK_INTERVAL_SECONDS) { - return; - } - setPref(PrefKey.LAST_UPDATE_CHECK, now); - fetch("https://api.github.com/repos/redphx/better-xcloud/releases/latest").then((response) => response.json()).then((json) => { - setPref(PrefKey.LATEST_VERSION, json.tag_name.substring(1)); - setPref(PrefKey.CURRENT_VERSION, SCRIPT_VERSION); - }); - Translations.updateTranslations(true); -} -function disablePwa() { - const userAgent2 = (window.navigator.orgUserAgent || window.navigator.userAgent || "").toLowerCase(); - if (!userAgent2) { - return; - } - if (!!AppInterface || UserAgent.isSafari(true)) { - Object.defineProperty(window.navigator, "standalone", { - value: true - }); - } -} -function hashCode(str2) { - let hash = 0; - for (let i = 0, len = str2.length;i < len; i++) { - const chr = str2.charCodeAt(i); - hash = (hash << 5) - hash + chr; - hash |= 0; - } - return hash; -} -function renderString(str2, obj) { - return str2.replace(/\$\{.+?\}/g, (match) => { - const key = match.substring(2, match.length - 1); - if (key in obj) { - return obj[key]; - } - return match; - }); -} - // src/modules/patches/controller-shortcuts.js var controller_shortcuts_default = "const currentGamepad = ${gamepadVar};\n\n// Share button on XS controller\nif (currentGamepad.buttons[17] && currentGamepad.buttons[17].value === 1) {\n window.dispatchEvent(new Event(BxEvent.CAPTURE_SCREENSHOT));\n}\n\nconst btnHome = currentGamepad.buttons[16];\nif (btnHome) {\n if (!this.bxHomeStates) {\n this.bxHomeStates = {};\n }\n\n if (btnHome.pressed) {\n this.gamepadIsIdle.set(currentGamepad.index, false);\n\n if (this.bxHomeStates[currentGamepad.index]) {\n const lastTimestamp = this.bxHomeStates[currentGamepad.index].timestamp;\n\n if (currentGamepad.timestamp !== lastTimestamp) {\n this.bxHomeStates[currentGamepad.index].timestamp = currentGamepad.timestamp;\n\n const handled = window.BX_EXPOSED.handleControllerShortcut(currentGamepad);\n if (handled) {\n this.bxHomeStates[currentGamepad.index].shortcutPressed += 1;\n }\n }\n } else {\n // First time pressing > save current timestamp\n window.BX_EXPOSED.resetControllerShortcut(currentGamepad.index);\n this.bxHomeStates[currentGamepad.index] = {\n shortcutPressed: 0,\n timestamp: currentGamepad.timestamp,\n };\n }\n\n // Listen to next button press\n const intervalMs = 50;\n this.inputConfiguration.useIntervalWorkerThreadForInput && this.intervalWorker ? this.intervalWorker.scheduleTimer(intervalMs) : this.pollGamepadssetTimeoutTimerID = setTimeout(this.pollGamepads, intervalMs);\n\n // Hijack this button\n return;\n } else if (this.bxHomeStates[currentGamepad.index]) {\n const info = structuredClone(this.bxHomeStates[currentGamepad.index]);\n\n // Home button released\n this.bxHomeStates[currentGamepad.index] = null;\n\n if (info.shortcutPressed === 0) {\n const fakeGamepadMappings = [{\n GamepadIndex: currentGamepad.index,\n A: 0,\n B: 0,\n X: 0,\n Y: 0,\n LeftShoulder: 0,\n RightShoulder: 0,\n LeftTrigger: 0,\n RightTrigger: 0,\n View: 0,\n Menu: 0,\n LeftThumb: 0,\n RightThumb: 0,\n DPadUp: 0,\n DPadDown: 0,\n DPadLeft: 0,\n DPadRight: 0,\n Nexus: 1,\n LeftThumbXAxis: 0,\n LeftThumbYAxis: 0,\n RightThumbXAxis: 0,\n RightThumbYAxis: 0,\n PhysicalPhysicality: 0,\n VirtualPhysicality: 0,\n Dirty: true,\n Virtual: false,\n }];\n\n const isLongPress = (currentGamepad.timestamp - info.timestamp) >= 500;\n const intervalMs = isLongPress ? 500 : 100;\n\n this.inputSink.onGamepadInput(performance.now() - intervalMs, fakeGamepadMappings);\n this.inputConfiguration.useIntervalWorkerThreadForInput && this.intervalWorker ? this.intervalWorker.scheduleTimer(intervalMs) : this.pollGamepadssetTimeoutTimerID = setTimeout(this.pollGamepads, intervalMs);\n return;\n }\n }\n}\n";