diff options
Diffstat (limited to 'platform/web/os_web.cpp')
-rw-r--r-- | platform/web/os_web.cpp | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp new file mode 100644 index 0000000000..461dc71119 --- /dev/null +++ b/platform/web/os_web.cpp @@ -0,0 +1,275 @@ +/*************************************************************************/ +/* os_web.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "os_web.h" + +#include "core/debugger/engine_debugger.h" +#include "drivers/unix/dir_access_unix.h" +#include "drivers/unix/file_access_unix.h" +#include "main/main.h" +#include "platform/web/display_server_web.h" + +#include "modules/modules_enabled.gen.h" // For websocket. +#ifdef MODULE_WEBSOCKET_ENABLED +#include "modules/websocket/remote_debugger_peer_websocket.h" +#endif + +#include <dlfcn.h> +#include <emscripten.h> +#include <stdlib.h> + +#include "api/javascript_singleton.h" +#include "godot_js.h" + +void OS_Web::alert(const String &p_alert, const String &p_title) { + godot_js_display_alert(p_alert.utf8().get_data()); +} + +// Lifecycle +void OS_Web::initialize() { + OS_Unix::initialize_core(); + DisplayServerWeb::register_web_driver(); + +#ifdef MODULE_WEBSOCKET_ENABLED + EngineDebugger::register_uri_handler("ws://", RemoteDebuggerPeerWebSocket::create); + EngineDebugger::register_uri_handler("wss://", RemoteDebuggerPeerWebSocket::create); +#endif +} + +void OS_Web::resume_audio() { + AudioDriverWeb::resume(); +} + +void OS_Web::set_main_loop(MainLoop *p_main_loop) { + main_loop = p_main_loop; +} + +MainLoop *OS_Web::get_main_loop() const { + return main_loop; +} + +void OS_Web::fs_sync_callback() { + get_singleton()->idb_is_syncing = false; +} + +bool OS_Web::main_loop_iterate() { + if (is_userfs_persistent() && idb_needs_sync && !idb_is_syncing) { + idb_is_syncing = true; + idb_needs_sync = false; + godot_js_os_fs_sync(&fs_sync_callback); + } + + DisplayServer::get_singleton()->process_events(); + + return Main::iteration(); +} + +void OS_Web::delete_main_loop() { + if (main_loop) { + memdelete(main_loop); + } + main_loop = nullptr; +} + +void OS_Web::finalize() { + delete_main_loop(); + for (AudioDriverWeb *driver : audio_drivers) { + memdelete(driver); + } + audio_drivers.clear(); +} + +// Miscellaneous + +Error OS_Web::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) { + return create_process(p_path, p_arguments); +} + +Error OS_Web::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) { + Array args; + for (const String &E : p_arguments) { + args.push_back(E); + } + String json_args = Variant(args).to_json_string(); + int failed = godot_js_os_execute(json_args.utf8().get_data()); + ERR_FAIL_COND_V_MSG(failed, ERR_UNAVAILABLE, "OS::execute() or create_process() must be implemented in Web via 'engine.setOnExecute' if required."); + return OK; +} + +Error OS_Web::kill(const ProcessID &p_pid) { + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "OS::kill() is not available on the Web platform."); +} + +int OS_Web::get_process_id() const { + ERR_FAIL_V_MSG(0, "OS::get_process_id() is not available on the Web platform."); +} + +bool OS_Web::is_process_running(const ProcessID &p_pid) const { + return false; +} + +int OS_Web::get_processor_count() const { + return godot_js_os_hw_concurrency_get(); +} + +bool OS_Web::_check_internal_feature_support(const String &p_feature) { + if (p_feature == "html5" || p_feature == "web") { + return true; + } + +#ifdef JAVASCRIPT_EVAL_ENABLED + if (p_feature == "web") { + return true; + } +#endif +#ifndef NO_THREADS + if (p_feature == "threads") { + return true; + } +#endif +#if WASM_GDNATIVE + if (p_feature == "wasm32") { + return true; + } +#endif + + return false; +} + +String OS_Web::get_executable_path() const { + return OS::get_executable_path(); +} + +Error OS_Web::shell_open(String p_uri) { + // Open URI in a new tab, browser will deal with it by protocol. + godot_js_os_shell_open(p_uri.utf8().get_data()); + return OK; +} + +String OS_Web::get_name() const { + return "Web"; +} + +void OS_Web::vibrate_handheld(int p_duration_ms) { + godot_js_input_vibrate_handheld(p_duration_ms); +} + +String OS_Web::get_user_data_dir() const { + return "/userfs"; +} + +String OS_Web::get_cache_path() const { + return "/home/web_user/.cache"; +} + +String OS_Web::get_config_path() const { + return "/home/web_user/.config"; +} + +String OS_Web::get_data_path() const { + return "/home/web_user/.local/share"; +} + +void OS_Web::file_access_close_callback(const String &p_file, int p_flags) { + OS_Web *os = OS_Web::get_singleton(); + if (!(os->is_userfs_persistent() && (p_flags & FileAccess::WRITE))) { + return; // FS persistence is not working or we are not writing. + } + bool is_file_persistent = p_file.begins_with("/userfs"); +#ifdef TOOLS_ENABLED + // Hack for editor persistence (can we track). + is_file_persistent = is_file_persistent || p_file.begins_with("/home/web_user/"); +#endif + if (is_file_persistent) { + os->idb_needs_sync = true; + } +} + +void OS_Web::update_pwa_state_callback() { + if (OS_Web::get_singleton()) { + OS_Web::get_singleton()->pwa_is_waiting = true; + } + if (JavaScript::get_singleton()) { + JavaScript::get_singleton()->emit_signal("pwa_update_available"); + } +} + +Error OS_Web::pwa_update() { + return godot_js_pwa_update() ? FAILED : OK; +} + +bool OS_Web::is_userfs_persistent() const { + return idb_available; +} + +Error OS_Web::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) { + String path = p_path.get_file(); + p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); + ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ". Error: " + dlerror()); + + if (r_resolved_path != nullptr) { + *r_resolved_path = path; + } + + return OK; +} + +OS_Web *OS_Web::get_singleton() { + return static_cast<OS_Web *>(OS::get_singleton()); +} + +void OS_Web::initialize_joypads() { +} + +OS_Web::OS_Web() { + char locale_ptr[16]; + godot_js_config_locale_get(locale_ptr, 16); + setenv("LANG", locale_ptr, true); + + godot_js_pwa_cb(&OS_Web::update_pwa_state_callback); + + if (AudioDriverWeb::is_available()) { +#ifdef NO_THREADS + audio_drivers.push_back(memnew(AudioDriverScriptProcessor)); +#endif + audio_drivers.push_back(memnew(AudioDriverWorklet)); + } + for (int i = 0; i < audio_drivers.size(); i++) { + AudioDriverManager::add_driver(audio_drivers[i]); + } + + idb_available = godot_js_os_fs_is_persistent(); + + Vector<Logger *> loggers; + loggers.push_back(memnew(StdLogger)); + _set_logger(memnew(CompositeLogger(loggers))); + + FileAccessUnix::close_notification_func = file_access_close_callback; +} |