Skip to content

Commit

Permalink
Merge pull request #42266 from Faless/js/3.0_sync_fs_size_handlers
Browse files Browse the repository at this point in the history
[3.2] [HTML5] Synchronous main, better persistence, handlers fixes, optional full screen.
  • Loading branch information
akien-mga authored Oct 2, 2020
2 parents de067cf + 35fcc18 commit 7ef6aa7
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 108 deletions.
29 changes: 20 additions & 9 deletions misc/dist/html/full-size.html
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
const EXECUTABLE_NAME = '$GODOT_BASENAME';
const MAIN_PACK = '$GODOT_BASENAME.pck';
const INDETERMINATE_STATUS_STEP_MS = 100;
const FULL_WINDOW = $GODOT_FULL_WINDOW;

var canvas = document.getElementById('canvas');
var statusProgress = document.getElementById('status-progress');
Expand All @@ -155,6 +156,9 @@

var initializing = true;
var statusMode = 'hidden';
var lastWidth = 0;
var lastHeight = 0;
var lastScale = 0;

var animationCallbacks = [];
function animate(time) {
Expand All @@ -164,16 +168,23 @@
requestAnimationFrame(animate);

function adjustCanvasDimensions() {
var scale = window.devicePixelRatio || 1;
var width = window.innerWidth;
var height = window.innerHeight;
canvas.width = width * scale;
canvas.height = height * scale;
canvas.style.width = width + "px";
canvas.style.height = height + "px";
const scale = window.devicePixelRatio || 1;
if (lastWidth != window.innerWidth || lastHeight != window.innerHeight || lastScale != scale) {
lastScale = scale;
lastWidth = window.innerWidth;
lastHeight = window.innerHeight;
canvas.width = Math.floor(lastWidth * scale);
canvas.height = Math.floor(lastHeight * scale);
canvas.style.width = lastWidth + "px";
canvas.style.height = lastHeight + "px";
}
}
if (FULL_WINDOW) {
animationCallbacks.push(adjustCanvasDimensions);
adjustCanvasDimensions();
} else {
engine.setCanvasResizedOnStart(true);
}
animationCallbacks.push(adjustCanvasDimensions);
adjustCanvasDimensions();

setStatusMode = function setStatusMode(mode) {

Expand Down
20 changes: 14 additions & 6 deletions platform/javascript/engine/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Function('return this')()['Engine'] = (function() {
this.resizeCanvasOnStart = false;
this.onExecute = null;
this.onExit = null;
this.persistentPaths = [];
};

Engine.prototype.init = /** @param {string=} basePath */ function(basePath) {
Expand All @@ -56,12 +57,14 @@ Function('return this')()['Engine'] = (function() {
config['locateFile'] = Utils.createLocateRewrite(loadPath);
config['instantiateWasm'] = Utils.createInstantiatePromise(loadPromise);
Godot(config).then(function(module) {
me.rtenv = module;
if (unloadAfterInit) {
unload();
}
resolve();
config = null;
module['initFS'](me.persistentPaths).then(function(fs_err) {
me.rtenv = module;
if (unloadAfterInit) {
unload();
}
resolve();
config = null;
});
});
});
return initPromise;
Expand Down Expand Up @@ -220,6 +223,10 @@ Function('return this')()['Engine'] = (function() {
this.rtenv['copyToFS'](path, buffer);
}

Engine.prototype.setPersistentPaths = function(persistentPaths) {
this.persistentPaths = persistentPaths;
};

// Closure compiler exported engine methods.
/** @export */
Engine['isWebGLAvailable'] = Utils.isWebGLAvailable;
Expand All @@ -241,5 +248,6 @@ Function('return this')()['Engine'] = (function() {
Engine.prototype['setOnExecute'] = Engine.prototype.setOnExecute;
Engine.prototype['setOnExit'] = Engine.prototype.setOnExit;
Engine.prototype['copyToFS'] = Engine.prototype.copyToFS;
Engine.prototype['setPersistentPaths'] = Engine.prototype.setPersistentPaths;
return Engine;
})();
2 changes: 2 additions & 0 deletions platform/javascript/export/export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
current_line = current_line.replace("$GODOT_BASENAME", p_name);
current_line = current_line.replace("$GODOT_PROJECT_NAME", ProjectSettings::get_singleton()->get_setting("application/config/name"));
current_line = current_line.replace("$GODOT_HEAD_INCLUDE", p_preset->get("html/head_include"));
current_line = current_line.replace("$GODOT_FULL_WINDOW", p_preset->get("html/full_window_size") ? "true" : "false");
current_line = current_line.replace("$GODOT_DEBUG_ENABLED", p_debug ? "true" : "false");
str_export += current_line + "\n";
}
Expand Down Expand Up @@ -290,6 +291,7 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/full_window_size"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
}
Expand Down
70 changes: 34 additions & 36 deletions platform/javascript/javascript_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ extern "C" EMSCRIPTEN_KEEPALIVE void _drop_files_callback(char *p_filev[], int p
os->get_main_loop()->drop_files(files);
}

extern "C" EMSCRIPTEN_KEEPALIVE void _request_quit_callback(char *p_filev[], int p_filec) {
if (os && os->get_main_loop()) {
os->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
}
}

void exit_callback() {
emscripten_cancel_main_loop(); // After this, we can exit!
Main::cleanup();
Expand Down Expand Up @@ -88,26 +94,36 @@ void main_loop_callback() {
/* clang-format on */
os->get_main_loop()->finish();
os->finalize_async(); // Will add all the async finish functions.
/* clang-format off */
EM_ASM({
Promise.all(Module.async_finish).then(function() {
Module.async_finish = [];
return new Promise(function(accept, reject) {
if (!Module.idbfs) {
accept();
return;
}
FS.syncfs(function(error) {
if (error) {
err('Failed to save IDB file system: ' + error.message);
}
accept();
});
});
}).then(function() {
ccall("cleanup_after_sync", null, []);
});
});
/* clang-format on */
}
}

extern "C" EMSCRIPTEN_KEEPALIVE void cleanup_after_sync() {
emscripten_set_main_loop(exit_callback, -1, false);
}

extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
// Set IDBFS status
String idbfs_err = String::utf8(p_idbfs_err);
if (!idbfs_err.empty()) {
print_line("IndexedDB not available: " + idbfs_err);
}
os->set_idb_available(idbfs_err.empty());
int main(int argc, char *argv[]) {
os = new OS_JavaScript(argc, argv);

// Set canvas ID
char canvas_ptr[256];
Expand All @@ -127,40 +143,22 @@ extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
/* clang-format on */
setenv("LANG", locale_ptr, true);

Main::setup2();
// Set IDBFS status
os->set_idb_available((bool)EM_ASM_INT({ return Module.idbfs }));

Main::setup(argv[0], argc - 1, &argv[1]);
// Ease up compatibility.
ResourceLoader::set_abort_on_missing_resources(false);
Main::start();
os->get_main_loop()->init();
// Immediately run the first iteration.
// We are inside an animation frame, we want to immediately draw on the newly setup canvas.
main_loop_callback();
emscripten_resume_main_loop();
}

int main(int argc, char *argv[]) {
// Create and mount userfs immediately.
// Expose method for requesting quit.
EM_ASM({
FS.mkdir('/userfs');
FS.mount(IDBFS, {}, '/userfs');
Module['request_quit'] = function() {
ccall("_request_quit_callback", null, []);
};
});
os = new OS_JavaScript(argc, argv);
Main::setup(argv[0], argc - 1, &argv[1], false);
emscripten_set_main_loop(main_loop_callback, -1, false);
emscripten_pause_main_loop(); // Will need to wait for FS sync.

// Sync from persistent state into memory and then
// run the 'main_after_fs_sync' function.
/* clang-format off */
EM_ASM({
FS.syncfs(true, function(err) {
requestAnimationFrame(function() {
ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""]);
});
});
});
/* clang-format on */

return 0;
// Continued async in main_after_fs_sync() from the syncfs() callback.
// Immediately run the first iteration.
// We are inside an animation frame, we want to immediately draw on the newly setup canvas.
main_loop_callback();
}
75 changes: 74 additions & 1 deletion platform/javascript/native/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,38 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/

Module['initFS'] = function(persistentPaths) {
FS.mkdir('/userfs');
FS.mount(IDBFS, {}, '/userfs');

function createRecursive(dir) {
try {
FS.stat(dir);
} catch (e) {
if (e.errno !== ERRNO_CODES.ENOENT) {
throw e;
}
FS.mkdirTree(dir);
}
}

persistentPaths.forEach(function(path) {
createRecursive(path);
FS.mount(IDBFS, {}, path);
});
return new Promise(function(resolve, reject) {
FS.syncfs(true, function(err) {
if (err) {
Module.idbfs = false;
console.log("IndexedDB not available: " + err.message);
} else {
Module.idbfs = true;
}
resolve(err);
});
});
};

Module['copyToFS'] = function(path, buffer) {
var p = path.lastIndexOf("/");
var dir = "/";
Expand All @@ -37,7 +69,7 @@ Module['copyToFS'] = function(path, buffer) {
try {
FS.stat(dir);
} catch (e) {
if (e.errno !== ERRNO_CODES.ENOENT) { // 'ENOENT', see https://github.com/emscripten-core/emscripten/blob/master/system/lib/libc/musl/arch/emscripten/bits/errno.h
if (e.errno !== ERRNO_CODES.ENOENT) {
throw e;
}
FS.mkdirTree(dir);
Expand Down Expand Up @@ -202,3 +234,44 @@ Module.drop_handler = (function() {
});
}
})();

function EventHandlers() {
function Handler(target, event, method, capture) {
this.target = target;
this.event = event;
this.method = method;
this.capture = capture;
}

var listeners = [];

function has(target, event, method, capture) {
return listeners.findIndex(function(e) {
return e.target === target && e.event === event && e.method === method && e.capture == capture;
}) !== -1;
}

this.add = function(target, event, method, capture) {
if (has(target, event, method, capture)) {
return;
}
listeners.push(new Handler(target, event, method, capture));
target.addEventListener(event, method, capture);
};

this.remove = function(target, event, method, capture) {
if (!has(target, event, method, capture)) {
return;
}
target.removeEventListener(event, method, capture);
};

this.clear = function() {
listeners.forEach(function(h) {
h.target.removeEventListener(h.event, h.method, h.capture);
});
listeners.length = 0;
};
}

Module.listeners = new EventHandlers();
Loading

0 comments on commit 7ef6aa7

Please sign in to comment.