mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-06 23:57:19 +02:00
Improve Stats bar (#115)
* Rename "RTT" to "PNG" * Show select box to select stats items * Toggle stats * Prevent select box from jumping around * Put Ping before FPS & remove "ms" suffix from Ping
This commit is contained in:
parent
eec41c58b6
commit
d1c724ff2c
@ -735,12 +735,19 @@ class StreamBadges {
|
|||||||
|
|
||||||
|
|
||||||
class StreamStats {
|
class StreamStats {
|
||||||
|
static get PING() { return 'ping'; }
|
||||||
|
static get FPS() { return 'fps'; }
|
||||||
|
static get BITRATE() { return 'btr'; }
|
||||||
|
static get DECODE_TIME() { return 'dt'; }
|
||||||
|
static get PACKETS_LOST() { return 'pl'; }
|
||||||
|
static get FRAMES_LOST() { return 'fl'; }
|
||||||
|
|
||||||
static #interval;
|
static #interval;
|
||||||
static #updateInterval = 1000;
|
static #updateInterval = 1000;
|
||||||
|
|
||||||
static #$container;
|
static #$container;
|
||||||
static #$fps;
|
static #$fps;
|
||||||
static #$rtt;
|
static #$ping;
|
||||||
static #$dt;
|
static #$dt;
|
||||||
static #$pl;
|
static #$pl;
|
||||||
static #$fl;
|
static #$fl;
|
||||||
@ -873,23 +880,25 @@ class StreamStats {
|
|||||||
} else if (stat.type === 'candidate-pair' && stat.state === 'succeeded') {
|
} else if (stat.type === 'candidate-pair' && stat.state === 'succeeded') {
|
||||||
// Round Trip Time
|
// Round Trip Time
|
||||||
const roundTripTime = typeof stat.currentRoundTripTime !== 'undefined' ? stat.currentRoundTripTime * 1000 : '???';
|
const roundTripTime = typeof stat.currentRoundTripTime !== 'undefined' ? stat.currentRoundTripTime * 1000 : '???';
|
||||||
StreamStats.#$rtt.textContent = `${roundTripTime}ms`;
|
StreamStats.#$ping.textContent = roundTripTime;
|
||||||
|
|
||||||
if (PREF_STATS_CONDITIONAL_FORMATTING) {
|
if (PREF_STATS_CONDITIONAL_FORMATTING) {
|
||||||
grade = (roundTripTime > 100) ? 'bad' : (roundTripTime > 75) ? 'ok' : (roundTripTime > 40) ? 'good' : '';
|
grade = (roundTripTime > 100) ? 'bad' : (roundTripTime > 75) ? 'ok' : (roundTripTime > 40) ? 'good' : '';
|
||||||
}
|
}
|
||||||
StreamStats.#$rtt.setAttribute('data-grade', grade);
|
StreamStats.#$ping.setAttribute('data-grade', grade);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static #refreshStyles() {
|
static #refreshStyles() {
|
||||||
|
const PREF_ITEMS = PREFS.get(Preferences.STATS_ITEMS);
|
||||||
const PREF_POSITION = PREFS.get(Preferences.STATS_POSITION);
|
const PREF_POSITION = PREFS.get(Preferences.STATS_POSITION);
|
||||||
const PREF_TRANSPARENT = PREFS.get(Preferences.STATS_TRANSPARENT);
|
const PREF_TRANSPARENT = PREFS.get(Preferences.STATS_TRANSPARENT);
|
||||||
const PREF_OPACITY = PREFS.get(Preferences.STATS_OPACITY);
|
const PREF_OPACITY = PREFS.get(Preferences.STATS_OPACITY);
|
||||||
const PREF_TEXT_SIZE = PREFS.get(Preferences.STATS_TEXT_SIZE);
|
const PREF_TEXT_SIZE = PREFS.get(Preferences.STATS_TEXT_SIZE);
|
||||||
|
|
||||||
|
StreamStats.#$container.setAttribute('data-stats', '[' + PREF_ITEMS.join('][') + ']');
|
||||||
StreamStats.#$container.setAttribute('data-position', PREF_POSITION);
|
StreamStats.#$container.setAttribute('data-position', PREF_POSITION);
|
||||||
StreamStats.#$container.setAttribute('data-transparent', PREF_TRANSPARENT);
|
StreamStats.#$container.setAttribute('data-transparent', PREF_TRANSPARENT);
|
||||||
StreamStats.#$container.style.opacity = PREF_OPACITY + '%';
|
StreamStats.#$container.style.opacity = PREF_OPACITY + '%';
|
||||||
@ -915,19 +924,22 @@ class StreamStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CE = createElement;
|
const CE = createElement;
|
||||||
StreamStats.#$container = CE('div', {'class': 'better-xcloud-stats-bar better-xcloud-gone'},
|
const STATS = {
|
||||||
CE('label', {}, 'FPS'),
|
[StreamStats.PING]: (StreamStats.#$ping = CE('span', {}, '0')),
|
||||||
StreamStats.#$fps = CE('span', {}, 0),
|
[StreamStats.FPS]: (StreamStats.#$fps = CE('span', {}, '0')),
|
||||||
CE('label', {}, 'RTT'),
|
[StreamStats.BITRATE]: (StreamStats.#$br = CE('span', {}, '0 Mbps')),
|
||||||
StreamStats.#$rtt = CE('span', {}, '0ms'),
|
[StreamStats.DECODE_TIME]: (StreamStats.#$dt = CE('span', {}, '0ms')),
|
||||||
CE('label', {}, 'DT'),
|
[StreamStats.PACKETS_LOST]: (StreamStats.#$pl = CE('span', {}, '0 (0.00%)')),
|
||||||
StreamStats.#$dt = CE('span', {}, '0ms'),
|
[StreamStats.FRAMES_LOST]: (StreamStats.#$fl = CE('span', {}, '0 (0.00%)')),
|
||||||
CE('label', {}, 'BR'),
|
};
|
||||||
StreamStats.#$br = CE('span', {}, '0 Mbps'),
|
|
||||||
CE('label', {}, 'PL'),
|
const $barFragment = document.createDocumentFragment();
|
||||||
StreamStats.#$pl = CE('span', {}, '0 (0.00%)'),
|
for (let statKey in STATS) {
|
||||||
CE('label', {}, 'FL'),
|
const $div = CE('div', {'class': `better-xcloud-stat-${statKey}`}, CE('label', {}, statKey.toUpperCase()), STATS[statKey]);
|
||||||
StreamStats.#$fl = CE('span', {}, '0 (0.00%)'));
|
$barFragment.appendChild($div);
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamStats.#$container = CE('div', {'class': 'better-xcloud-stats-bar better-xcloud-gone'}, $barFragment);
|
||||||
|
|
||||||
let clickTimeout;
|
let clickTimeout;
|
||||||
StreamStats.#$container.addEventListener('mousedown', e => {
|
StreamStats.#$container.addEventListener('mousedown', e => {
|
||||||
@ -949,48 +961,59 @@ class StreamStats {
|
|||||||
const refreshFunc = e => {
|
const refreshFunc = e => {
|
||||||
StreamStats.#refreshStyles()
|
StreamStats.#refreshStyles()
|
||||||
};
|
};
|
||||||
const $position = PREFS.toElement(Preferences.STATS_POSITION, refreshFunc);
|
|
||||||
|
|
||||||
let $close;
|
let $close;
|
||||||
const $showStartup = PREFS.toElement(Preferences.STATS_SHOW_WHEN_PLAYING);
|
|
||||||
const $quickGlance = PREFS.toElement(Preferences.STATS_QUICK_GLANCE, e => {
|
|
||||||
e.target.checked ? StreamStats.quickGlanceSetup() : StreamStats.quickGlanceStop();
|
const STATS_UI = {
|
||||||
});
|
[Preferences.STATS_SHOW_WHEN_PLAYING]: {
|
||||||
const $transparent = PREFS.toElement(Preferences.STATS_TRANSPARENT, refreshFunc);
|
'label': 'Show stats when starting the game',
|
||||||
const $formatting = PREFS.toElement(Preferences.STATS_CONDITIONAL_FORMATTING, refreshFunc);
|
},
|
||||||
const $opacity = PREFS.toElement(Preferences.STATS_OPACITY, refreshFunc);
|
[Preferences.STATS_QUICK_GLANCE]: {
|
||||||
const $textSize = PREFS.toElement(Preferences.STATS_TEXT_SIZE, refreshFunc);
|
'label': 'Enable "Quick Glance" mode',
|
||||||
|
'onChange': e => {
|
||||||
|
e.target.checked ? StreamStats.quickGlanceSetup() : StreamStats.quickGlanceStop();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[Preferences.STATS_ITEMS]: {
|
||||||
|
'label': 'Stats',
|
||||||
|
'onChange': refreshFunc,
|
||||||
|
},
|
||||||
|
[Preferences.STATS_POSITION]: {
|
||||||
|
'label': 'Position',
|
||||||
|
'onChange': refreshFunc,
|
||||||
|
},
|
||||||
|
[Preferences.STATS_TEXT_SIZE]: {
|
||||||
|
'label': 'Text size',
|
||||||
|
'onChange': refreshFunc,
|
||||||
|
},
|
||||||
|
[Preferences.STATS_OPACITY]: {
|
||||||
|
'label': 'Opacity (50-100%)',
|
||||||
|
'onChange': refreshFunc,
|
||||||
|
},
|
||||||
|
[Preferences.STATS_TRANSPARENT]: {
|
||||||
|
'label': 'Transparent background',
|
||||||
|
'onChange': refreshFunc,
|
||||||
|
},
|
||||||
|
[Preferences.STATS_CONDITIONAL_FORMATTING]: {
|
||||||
|
'label': 'Conditional formatting text color',
|
||||||
|
'onChange': refreshFunc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const $fragment = document.createDocumentFragment();
|
||||||
|
for (let settingKey in STATS_UI) {
|
||||||
|
const setting = STATS_UI[settingKey];
|
||||||
|
|
||||||
|
$fragment.appendChild(CE('div', {},
|
||||||
|
CE('label', {'for': `xcloud_setting_${settingKey}`}, setting.label),
|
||||||
|
PREFS.toElement(settingKey, setting.onChange)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
StreamStats.#$settings = CE('div', {'class': 'better-xcloud-stats-settings'},
|
StreamStats.#$settings = CE('div', {'class': 'better-xcloud-stats-settings'},
|
||||||
CE('b', {}, 'Stream Stats Settings'),
|
CE('b', {}, 'Stream Stats Settings'),
|
||||||
CE('div', {},
|
$fragment,
|
||||||
CE('label', {'for': `xcloud_setting_${Preferences.STATS_SHOW_WHEN_PLAYING}`}, 'Show stats when starting the game'),
|
|
||||||
$showStartup
|
|
||||||
),
|
|
||||||
CE('div', {},
|
|
||||||
CE('label', {'for': `xcloud_setting_${Preferences.STATS_QUICK_GLANCE}`}, 'Enable "Quick Glance" mode'),
|
|
||||||
$quickGlance
|
|
||||||
),
|
|
||||||
CE('div', {},
|
|
||||||
CE('label', {}, 'Position'),
|
|
||||||
$position
|
|
||||||
),
|
|
||||||
CE('div', {},
|
|
||||||
CE('label', {}, 'Text size'),
|
|
||||||
$textSize
|
|
||||||
),
|
|
||||||
CE('div', {},
|
|
||||||
CE('label', {'for': `xcloud_setting_${Preferences.STATS_OPACITY}`}, 'Opacity (50-100%)'),
|
|
||||||
$opacity
|
|
||||||
),
|
|
||||||
CE('div', {},
|
|
||||||
CE('label', {'for': `xcloud_setting_${Preferences.STATS_TRANSPARENT}`}, 'Transparent background'),
|
|
||||||
$transparent
|
|
||||||
),
|
|
||||||
CE('div', {},
|
|
||||||
CE('label', {'for': `xcloud_setting_${Preferences.STATS_CONDITIONAL_FORMATTING}`}, 'Conditional formatting text color'),
|
|
||||||
$formatting
|
|
||||||
),
|
|
||||||
$close = CE('button', {}, 'Close'));
|
$close = CE('button', {}, 'Close'));
|
||||||
|
|
||||||
$close.addEventListener('click', e => StreamStats.hideSettingsUi());
|
$close.addEventListener('click', e => StreamStats.hideSettingsUi());
|
||||||
@ -1133,6 +1156,7 @@ class Preferences {
|
|||||||
|
|
||||||
static get AUDIO_MIC_ON_PLAYING() { return 'audio_mic_on_playing'; }
|
static get AUDIO_MIC_ON_PLAYING() { return 'audio_mic_on_playing'; }
|
||||||
|
|
||||||
|
static get STATS_ITEMS() { return 'stats_items'; };
|
||||||
static get STATS_SHOW_WHEN_PLAYING() { return 'stats_show_when_playing'; }
|
static get STATS_SHOW_WHEN_PLAYING() { return 'stats_show_when_playing'; }
|
||||||
static get STATS_QUICK_GLANCE() { return 'stats_quick_glance'; }
|
static get STATS_QUICK_GLANCE() { return 'stats_quick_glance'; }
|
||||||
static get STATS_POSITION() { return 'stats_position'; }
|
static get STATS_POSITION() { return 'stats_position'; }
|
||||||
@ -1310,6 +1334,18 @@ class Preferences {
|
|||||||
[Preferences.AUDIO_MIC_ON_PLAYING]: {
|
[Preferences.AUDIO_MIC_ON_PLAYING]: {
|
||||||
'default': false,
|
'default': false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[Preferences.STATS_ITEMS]: {
|
||||||
|
'default': [StreamStats.PING, StreamStats.FPS, StreamStats.BITRATE, StreamStats.DECODE_TIME, StreamStats.PACKETS_LOST, StreamStats.FRAMES_LOST],
|
||||||
|
'multiple_options': {
|
||||||
|
[StreamStats.PING]: 'Ping',
|
||||||
|
[StreamStats.FPS]: 'FPS',
|
||||||
|
[StreamStats.BITRATE]: 'Bitrate',
|
||||||
|
[StreamStats.DECODE_TIME]: 'Decode time',
|
||||||
|
[StreamStats.PACKETS_LOST]: 'Packets lost',
|
||||||
|
[StreamStats.FRAMES_LOST]: 'Frames lost',
|
||||||
|
},
|
||||||
|
},
|
||||||
[Preferences.STATS_SHOW_WHEN_PLAYING]: {
|
[Preferences.STATS_SHOW_WHEN_PLAYING]: {
|
||||||
'default': false,
|
'default': false,
|
||||||
},
|
},
|
||||||
@ -1410,6 +1446,17 @@ class Preferences {
|
|||||||
|
|
||||||
if ('options' in config && !(value in config.options)) {
|
if ('options' in config && !(value in config.options)) {
|
||||||
value = config.default;
|
value = config.default;
|
||||||
|
} else if ('multiple_options' in config) {
|
||||||
|
if (value.length) {
|
||||||
|
const validOptions = Object.keys(config.multiple_options);
|
||||||
|
value.forEach((item, idx) => {
|
||||||
|
(validOptions.indexOf(item) === -1) && value.splice(idx, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value.length) {
|
||||||
|
value = config.default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1428,7 +1475,7 @@ class Preferences {
|
|||||||
|
|
||||||
let $control;
|
let $control;
|
||||||
if ('options' in setting) {
|
if ('options' in setting) {
|
||||||
$control = CE('select', {id: 'xcloud_setting_' + key});
|
$control = CE('select', {'id': 'xcloud_setting_' + key});
|
||||||
for (let value in setting.options) {
|
for (let value in setting.options) {
|
||||||
const label = setting.options[value];
|
const label = setting.options[value];
|
||||||
|
|
||||||
@ -1439,6 +1486,39 @@ class Preferences {
|
|||||||
$control.value = currentValue;
|
$control.value = currentValue;
|
||||||
$control.addEventListener('change', e => {
|
$control.addEventListener('change', e => {
|
||||||
PREFS.set(key, e.target.value);
|
PREFS.set(key, e.target.value);
|
||||||
|
onChange && onChange(e);
|
||||||
|
});
|
||||||
|
} else if ('multiple_options' in setting) {
|
||||||
|
$control = CE('select', {'id': 'xcloud_setting_' + key, 'multiple': true});
|
||||||
|
for (let value in setting.multiple_options) {
|
||||||
|
const label = setting.multiple_options[value];
|
||||||
|
|
||||||
|
const $option = CE('option', {value: value}, label);
|
||||||
|
$option.selected = currentValue.indexOf(value) > -1;
|
||||||
|
|
||||||
|
$control.appendChild($option);
|
||||||
|
}
|
||||||
|
|
||||||
|
$control.addEventListener('mousedown', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const $this = this;
|
||||||
|
const orgScrollTop = $this.scrollTop;
|
||||||
|
e.target.selected = !e.target.selected;
|
||||||
|
|
||||||
|
const $parent = e.target.parentElement;
|
||||||
|
$parent.focus();
|
||||||
|
$parent.dispatchEvent(new Event('change'));
|
||||||
|
|
||||||
|
setTimeout(() => ($this.scrollTop = orgScrollTop), 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
$control.addEventListener('mousemove', e => e.preventDefault());
|
||||||
|
|
||||||
|
// $control.value = currentValue;
|
||||||
|
$control.addEventListener('change', e => {
|
||||||
|
const values = Array.from(e.target.selectedOptions).map(e => e.value);
|
||||||
|
PREFS.set(key, values);
|
||||||
|
|
||||||
onChange && onChange(e);
|
onChange && onChange(e);
|
||||||
});
|
});
|
||||||
} else if (typeof setting.default === 'number') {
|
} else if (typeof setting.default === 'number') {
|
||||||
@ -1746,6 +1826,32 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
|
|||||||
text-wrap: nowrap;
|
text-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.better-xcloud-stats-bar > div {
|
||||||
|
display: none;
|
||||||
|
margin-right: 8px;
|
||||||
|
border-right: 2px solid #fff;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.better-xcloud-stats-bar[data-stats*="[fps]"] > .better-xcloud-stat-fps,
|
||||||
|
.better-xcloud-stats-bar[data-stats*="[ping]"] > .better-xcloud-stat-ping,
|
||||||
|
.better-xcloud-stats-bar[data-stats*="[btr]"] > .better-xcloud-stat-btr,
|
||||||
|
.better-xcloud-stats-bar[data-stats*="[dt]"] > .better-xcloud-stat-dt,
|
||||||
|
.better-xcloud-stats-bar[data-stats*="[pl]"] > .better-xcloud-stat-pl,
|
||||||
|
.better-xcloud-stats-bar[data-stats*="[fl]"] > .better-xcloud-stat-fl {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.better-xcloud-stats-bar[data-stats$="[fps]"] > .better-xcloud-stat-fps,
|
||||||
|
.better-xcloud-stats-bar[data-stats$="[ping]"] > .better-xcloud-stat-ping,
|
||||||
|
.better-xcloud-stats-bar[data-stats$="[btr]"] > .better-xcloud-stat-btr,
|
||||||
|
.better-xcloud-stats-bar[data-stats$="[dt]"] > .better-xcloud-stat-dt,
|
||||||
|
.better-xcloud-stats-bar[data-stats$="[pl]"] > .better-xcloud-stat-pl,
|
||||||
|
.better-xcloud-stats-bar[data-stats$="[fl]"] > .better-xcloud-stat-fl {
|
||||||
|
margin-right: 0;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
.better-xcloud-stats-bar[data-display=glancing]::before {
|
.better-xcloud-stats-bar[data-display=glancing]::before {
|
||||||
content: '👀 ';
|
content: '👀 ';
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
@ -1753,15 +1859,18 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
|
|||||||
|
|
||||||
.better-xcloud-stats-bar[data-position=top-left] {
|
.better-xcloud-stats-bar[data-position=top-left] {
|
||||||
left: 0;
|
left: 0;
|
||||||
|
border-radius: 0 0 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.better-xcloud-stats-bar[data-position=top-right] {
|
.better-xcloud-stats-bar[data-position=top-right] {
|
||||||
right: 0;
|
right: 0;
|
||||||
|
border-radius: 0 0 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.better-xcloud-stats-bar[data-position=top-center] {
|
.better-xcloud-stats-bar[data-position=top-center] {
|
||||||
transform: translate(-50%, 0);
|
transform: translate(-50%, 0);
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.better-xcloud-stats-bar[data-transparent=true] {
|
.better-xcloud-stats-bar[data-transparent=true] {
|
||||||
@ -1781,9 +1890,6 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
|
|||||||
min-width: 60px;
|
min-width: 60px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
padding-right: 8px;
|
|
||||||
margin-right: 8px;
|
|
||||||
border-right: 2px solid #fff;
|
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1803,11 +1909,6 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
|
|||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.better-xcloud-stats-bar span:last-of-type {
|
|
||||||
border: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.better-xcloud-stats-settings {
|
.better-xcloud-stats-settings {
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user