diff --git a/web/css/ui.css b/web/css/ui.css
index 1a2d05fdc..ce3cfd27a 100644
--- a/web/css/ui.css
+++ b/web/css/ui.css
@@ -74,10 +74,13 @@
.settings__option-value select, .settings__option-value input {
font-family: '6809', monospace;
- width: 6em;
font-size: 90%;
}
+.settings__option-value input:not([type='checkbox']) {
+ width: 6em;
+}
+
.settings__option-value option {
font-size: 150%;
}
@@ -88,10 +91,15 @@
.keyboard-bindings .settings__option-value {
display: grid;
- grid-template-columns: 25% 25% auto auto;
+ grid-template-columns: 20% 20% 20% 20% auto;
row-gap: 5px;
}
+.settings__option-checkbox label {
+ display: inline-flex;
+ align-items: center;
+}
+
.binding-element {
display: flex;
flex-direction: column;
diff --git a/web/index.html b/web/index.html
index e710272f9..068279c6a 100644
--- a/web/index.html
+++ b/web/index.html
@@ -16,7 +16,7 @@
-
+
Cloud Retro
@@ -102,18 +102,18 @@
-
+
-
-
-
+
+
+
-
+
@@ -122,7 +122,7 @@
-
+
diff --git a/web/js/controller.js b/web/js/controller.js
index cd57844a3..e85733a07 100644
--- a/web/js/controller.js
+++ b/web/js/controller.js
@@ -138,6 +138,7 @@
input.poll.disable();
gui.hide(menuScreen);
stream.toggle(true);
+ stream.forceFullscreenMaybe();
gui.show(keyButtons[KEY.SAVE]);
gui.show(keyButtons[KEY.LOAD]);
// end clear
diff --git a/web/js/env.js b/web/js/env.js
index 0a6ddb80b..a8f79ebd6 100644
--- a/web/js/env.js
+++ b/web/js/env.js
@@ -108,8 +108,7 @@ const env = (() => {
return {
getOs: getOS,
getBrowser: getBrowser,
- // Check mobile type because different mobile can accept different video encoder.
- isMobileDevice: () => (typeof window.orientation !== 'undefined') || (navigator.userAgent.indexOf('IEMobile') !== -1),
+ isMobileDevice: () => /Mobi|Android|iPhone/i.test(navigator.userAgent),
display: () => ({
isPortrait: isPortrait,
toggleFullscreen: toggleFullscreen,
diff --git a/web/js/gui/gui.js b/web/js/gui/gui.js
index 239affde7..b58ca0a3b 100644
--- a/web/js/gui/gui.js
+++ b/web/js/gui/gui.js
@@ -42,6 +42,36 @@ const gui = (() => {
return el;
}
+ const checkbox = (id, cb = () => ({}), checked = false, label = '', cc = '') => {
+ const el = _create();
+ cc !== '' && el.classList.add(cc);
+
+ let parent = el;
+
+ if (label) {
+ const _label = _create('label', (el) => {
+ el.setAttribute('htmlFor', id);
+ })
+ _label.innerText = label;
+ el.append(_label)
+ parent = _label;
+ }
+
+ const input = _create('input', (el) => {
+ el.setAttribute('id', id);
+ el.setAttribute('name', id);
+ el.setAttribute('type', 'checkbox');
+ el.onclick = ((e) => {
+ checked = e.target.checked
+ cb(id, checked)
+ })
+ checked && el.setAttribute('checked', '');
+ });
+ parent.prepend(input);
+
+ return el;
+ }
+
const panel = (root, title = '', cc = '', content, buttons = [], onToggle) => {
const state = {
br: null,
@@ -221,6 +251,7 @@ const gui = (() => {
fadeInOut,
},
binding,
+ checkbox,
create: _create,
fragment,
hide,
diff --git a/web/js/settings/opts.js b/web/js/settings/opts.js
index b0b2b9662..5e9fac033 100644
--- a/web/js/settings/opts.js
+++ b/web/js/settings/opts.js
@@ -11,5 +11,6 @@ const opts = Object.freeze({
LOG_LEVEL: 'log.level',
INPUT_KEYBOARD_MAP: 'input.keyboard.map',
MIRROR_SCREEN: 'mirror.screen',
- VOLUME: 'volume'
+ VOLUME: 'volume',
+ FORCE_FULLSCREEN: 'force.fullscreen'
});
diff --git a/web/js/settings/settings.js b/web/js/settings/settings.js
index 01da6f1c4..09e1cb7f0 100644
--- a/web/js/settings/settings.js
+++ b/web/js/settings/settings.js
@@ -15,7 +15,7 @@
*/
const settings = (() => {
// internal structure version
- const revision = 1.5;
+ const revision = 1.51;
// default settings
// keep them for revert to defaults option
@@ -480,7 +480,7 @@ settings.renderer = (() => {
case opts.MIRROR_SCREEN:
_option(data).withName('Video mirroring')
.add(gui.select(k, onChange, {values: ['mirror'], labels: []}, value))
- .withDescription('Disables video image smoothing by rendering the video on a canvas (much more demanding on the CPU/GPU)')
+ .withDescription('Disables video image smoothing by rendering the video on a canvas (much more demanding for browser)')
.build();
break;
case opts.VOLUME:
@@ -489,6 +489,14 @@ settings.renderer = (() => {
.restartNeeded()
.build()
break;
+ case opts.FORCE_FULLSCREEN:
+ _option(data).withName('Force fullscreen')
+ .withDescription(
+ 'Whether games should open in full-screen mode after starting up (excluding mobile devices)'
+ )
+ .add(gui.checkbox(k, onChange, value, 'Enbabled', 'settings__option-checkbox'))
+ .build()
+ break;
default:
_option(data).withName(k).add(value).build();
}
diff --git a/web/js/stream/stream.js b/web/js/stream/stream.js
index c5dcf4782..b0fb730d8 100644
--- a/web/js/stream/stream.js
+++ b/web/js/stream/stream.js
@@ -12,6 +12,7 @@ const stream = (() => {
poster: '/img/screen_loading.gif',
mirrorMode: null,
mirrorUpdateRate: 1 / 60,
+ forceFullscreen: true,
},
state = {
screen: screen,
@@ -112,6 +113,12 @@ const stream = (() => {
screen.classList.toggle('no-media-controls', make)
}
+ const forceFullscreenMaybe = () => {
+ const touchMode = env.isMobileDevice();
+ log.debug('touch check', touchMode)
+ !touchMode && options.forceFullscreen && toggleFullscreen();
+ }
+
const useCustomScreen = (use) => {
if (use) {
if (screen.paused || screen.ended) return;
@@ -158,14 +165,20 @@ const stream = (() => {
const init = () => {
options.mirrorMode = settings.loadOr(opts.MIRROR_SCREEN, 'none');
options.volume = settings.loadOr(opts.VOLUME, 50) / 100;
+ options.forceFullscreen = settings.loadOr(opts.FORCE_FULLSCREEN, false);
}
event.sub(SETTINGS_CHANGED, () => {
- const newValue = settings.get()[opts.MIRROR_SCREEN];
+ const s = settings.get();
+ const newValue = s[opts.MIRROR_SCREEN];
if (newValue !== options.mirrorMode) {
useCustomScreen(newValue === 'mirror');
options.mirrorMode = newValue;
}
+ const newValue2 = s[opts.FORCE_FULLSCREEN];
+ if (newValue2 !== options.forceFullscreen) {
+ options.forceFullscreen = newValue2;
+ }
});
@@ -196,6 +209,7 @@ const stream = (() => {
play: stream,
toggle,
useCustomScreen,
+ forceFullscreenMaybe,
init
}
}