From 0b0f5add1bb631859ce25247712e569112109d7c Mon Sep 17 00:00:00 2001 From: redphx <96280+redphx@users.noreply.github.com> Date: Sun, 6 Aug 2023 18:51:06 +0700 Subject: [PATCH] Toggle touch controller visibility (#72) * Double-tap anywhere at the bottom of the screen to show/hide touch controller * Update CSS * Update the size of the screenshot button * Update screenshot button's CSS * Update screenshot button's opacity --- better-xcloud.user.js | 192 +++++++++++++++++++++++++++++++++--------- 1 file changed, 153 insertions(+), 39 deletions(-) diff --git a/better-xcloud.user.js b/better-xcloud.user.js index c7a32ab..fcc3a79 100644 --- a/better-xcloud.user.js +++ b/better-xcloud.user.js @@ -23,7 +23,6 @@ var $SCREENSHOT_CANVAS; var GAME_TITLE_ID; const TOUCH_SUPPORTED_GAME_IDS = new Set(); -var SHOW_GENERIC_TOUCH_CONTROLLER = false; // Credit: https://phosphoricons.com const ICON_VIDEO_SETTINGS = ''; @@ -31,6 +30,126 @@ const ICON_STREAM_STATS = ' { + TouchController.#dataChannel.dispatchEvent(msg); + }, 10); + } + + static setup() { + const $bar = createElement('div', {'id': 'better-xcloud-touch-controller-bar'}); + document.documentElement.appendChild($bar); + + // Setup double-tap event + let clickTimeout; + $bar.addEventListener('mousedown', e => { + clickTimeout && clearTimeout(clickTimeout); + if (clickTimeout) { + // Double-clicked + clickTimeout = null; + TouchController.#toggleVisibility(); + return; + } + + clickTimeout = setTimeout(() => { + clickTimeout = null; + }, 400); + }); + + TouchController.#$bar = $bar; + + RTCPeerConnection.prototype.orgCreateDataChannel = RTCPeerConnection.prototype.createDataChannel; + RTCPeerConnection.prototype.createDataChannel = function() { + const dataChannel = this.orgCreateDataChannel.apply(this, arguments); + if (!TouchController.#enable) { + return dataChannel; + } + + TouchController.#dataChannel = dataChannel; + + // Fix sometimes the touch controller doesn't show at the beginning + dataChannel.addEventListener('open', e => { + TouchController.#show(); + }); + + dataChannel.addEventListener('message', msg => { + if (msg.origin === 'better-xcloud' || typeof msg.data !== 'string') { + return; + } + + // Dispatch a message to display generic touch controller + if (msg.data.includes('touchcontrols/showtitledefault')) { + TouchController.#show(); + } + }); + + return dataChannel; + }; + } +} + class MouseCursorHider { static #timeout; static #cursorVisible = true; @@ -1157,9 +1276,13 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] { opacity: 0; position: fixed; bottom: 0; - width: 60px; - height: 60px; - padding: 12px; + box-sizing: border-box; + width: 16vh; + height: 16vh; + max-width: 128px; + max-height: 128px; + padding: 2vh; + padding: 24px 24px 12px 12px; background-size: cover; background-repeat: no-repeat; background-origin: content-box; @@ -1172,11 +1295,11 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] { } .better-xcloud-screenshot-button[data-showing=true] { - opacity: 1; + opacity: 0.9; } .better-xcloud-screenshot-button[data-capturing=true] { - padding: 6px; + padding: 1vh; } .better-xcloud-screenshot-canvas { @@ -1404,6 +1527,21 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] { font-family: Consolas, "Courier New", Courier, monospace; } +#better-xcloud-touch-controller-bar { + display: none; + opacity: 0; + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 6vh; + z-index: 5555; +} + +#better-xcloud-touch-controller-bar[data-showing=true] { + display: block !important; +} + /* Hide UI elements */ #headerArea, #uhfSkipToMain, .uhf-footer { display: none; @@ -1722,16 +1860,16 @@ function interceptHttpRequests() { } if (PREF_STREAM_TOUCH_CONTROLLER === 'all' && url.endsWith('/configuration') && url.includes('/sessions/cloud/') && request.method === 'GET') { - SHOW_GENERIC_TOUCH_CONTROLLER = false; + TouchController.disable(); // Get game ID from window.location const match = window.location.pathname.match(/\/launch\/[^\/]+\/([\w\d]+)/); // Check touch support if (match && !TOUCH_SUPPORTED_GAME_IDS.has(match[1])) { - SHOW_GENERIC_TOUCH_CONTROLLER = true; + TouchController.enable(); } const promise = orgFetch(...arg); - if (!SHOW_GENERIC_TOUCH_CONTROLLER) { + if (!TouchController.isEnabled()) { return promise; } @@ -2540,6 +2678,7 @@ function onHistoryChanged() { document.querySelector('.better-xcloud-screenshot-button').style = ''; MouseCursorHider.stop(); + TouchController.reset(); } @@ -2547,6 +2686,10 @@ function onStreamStarted($video) { // Get title ID for screenshot's name GAME_TITLE_ID = /\/launch\/([^/]+)/.exec(window.location.pathname)[1]; + if (TouchController.isEnabled()) { + TouchController.enableBar(); + } + const PREF_SCREENSHOT_BUTTON_POSITION = PREFS.get(Preferences.SCREENSHOT_BUTTON_POSITION); const PREF_STATS_QUICK_GLANCE = PREFS.get(Preferences.STATS_QUICK_GLANCE); @@ -2673,36 +2816,7 @@ RTCPeerConnection.prototype.addIceCandidate = function(...args) { } if (PREFS.get(Preferences.STREAM_TOUCH_CONTROLLER) === 'all') { - RTCPeerConnection.prototype.orgCreateDataChannel = RTCPeerConnection.prototype.createDataChannel; - RTCPeerConnection.prototype.createDataChannel = function() { - const dataChannel = this.orgCreateDataChannel.apply(this, arguments); - if (!SHOW_GENERIC_TOUCH_CONTROLLER) { - return dataChannel; - } - - const dispatchLayout = () => { - // Dispatch a message to display generic touch controller - dataChannel.dispatchEvent(new MessageEvent('message', { - data: '{"content":"{\\"layoutId\\":\\"\\"}","target":"/streaming/touchcontrols/showlayoutv2","type":"Message"}', - origin: 'better-xcloud', - })); - } - - // Fix sometimes the touch controller doesn't show at the beginning - setTimeout(dispatchLayout, 100); - - dataChannel.addEventListener('message', msg => { - if (msg.origin === 'better-xcloud' || typeof msg.data !== 'string') { - return; - } - - if (msg.data.includes('touchcontrols/showtitledefault')) { - setTimeout(dispatchLayout, 10); - } - }); - - return dataChannel; - }; + TouchController.setup(); }