summaryrefslogtreecommitdiff
path: root/platform/web/os_web.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/web/os_web.cpp')
-rw-r--r--platform/web/os_web.cpp275
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;
+}