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();
}