2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* os_javascript.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 02:10:30 +01:00
/* */
/* 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. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "os_javascript.h"
2017-01-16 19:19:45 +01:00
2020-03-16 17:02:14 +01:00
# include "core/debugger/engine_debugger.h"
2018-10-01 21:35:55 +02:00
# include "drivers/unix/dir_access_unix.h"
# include "drivers/unix/file_access_unix.h"
2014-02-10 02:10:30 +01:00
# include "main/main.h"
2020-05-01 14:45:45 +02:00
# include "platform/javascript/display_server_javascript.h"
2014-02-10 02:10:30 +01:00
2021-11-12 13:42:58 +01:00
# include "modules/modules_enabled.gen.h" // For websocket.
2020-03-16 17:02:14 +01:00
# ifdef MODULE_WEBSOCKET_ENABLED
# include "modules/websocket/remote_debugger_peer_websocket.h"
# endif
2020-12-05 00:37:41 +01:00
# include <dlfcn.h>
2017-01-16 19:19:45 +01:00
# include <emscripten.h>
2017-03-05 16:44:50 +01:00
# include <stdlib.h>
2017-01-16 19:19:45 +01:00
2022-01-31 15:28:12 +01:00
# include "api/javascript_singleton.h"
2020-10-23 18:33:20 +02:00
# include "godot_js.h"
2021-07-22 18:23:48 +02:00
void OS_JavaScript : : alert ( const String & p_alert , const String & p_title ) {
godot_js_display_alert ( p_alert . utf8 ( ) . get_data ( ) ) ;
}
2018-07-08 02:23:19 +02:00
// Lifecycle
2020-05-01 14:45:45 +02:00
void OS_JavaScript : : initialize ( ) {
2018-07-08 02:23:19 +02:00
OS_Unix : : initialize_core ( ) ;
2020-05-01 14:45:45 +02:00
DisplayServerJavaScript : : register_javascript_driver ( ) ;
2018-07-08 02:23:19 +02:00
2020-03-16 17:02:14 +01:00
# ifdef MODULE_WEBSOCKET_ENABLED
EngineDebugger : : register_uri_handler ( " ws:// " , RemoteDebuggerPeerWebSocket : : create ) ;
EngineDebugger : : register_uri_handler ( " wss:// " , RemoteDebuggerPeerWebSocket : : create ) ;
# endif
2020-05-01 14:45:45 +02:00
}
2018-01-05 16:37:31 +01:00
2020-05-01 14:45:45 +02:00
void OS_JavaScript : : resume_audio ( ) {
2021-09-12 19:23:30 +02:00
AudioDriverJavaScript : : resume ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-09-03 01:44:00 +02:00
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : set_main_loop ( MainLoop * p_main_loop ) {
main_loop = p_main_loop ;
}
MainLoop * OS_JavaScript : : get_main_loop ( ) const {
return main_loop ;
}
2020-10-23 18:33:20 +02:00
void OS_JavaScript : : fs_sync_callback ( ) {
get_singleton ( ) - > idb_is_syncing = false ;
2018-07-08 02:23:19 +02:00
}
2015-09-12 15:54:47 +02:00
2018-07-08 02:23:19 +02:00
bool OS_JavaScript : : main_loop_iterate ( ) {
2020-09-18 17:25:05 +02:00
if ( is_userfs_persistent ( ) & & idb_needs_sync & & ! idb_is_syncing ) {
idb_is_syncing = true ;
idb_needs_sync = false ;
2020-10-23 18:33:20 +02:00
godot_js_os_fs_sync ( & fs_sync_callback ) ;
2015-09-12 15:54:47 +02:00
}
2018-08-12 15:39:32 +02:00
2020-05-01 14:45:45 +02:00
DisplayServer : : get_singleton ( ) - > process_events ( ) ;
2018-08-12 15:39:32 +02:00
2014-02-10 02:10:30 +01:00
return Main : : iteration ( ) ;
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : delete_main_loop ( ) {
2020-05-01 14:45:45 +02:00
if ( main_loop ) {
memdelete ( main_loop ) ;
}
main_loop = nullptr ;
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : finalize ( ) {
2020-05-01 14:45:45 +02:00
delete_main_loop ( ) ;
2021-09-12 19:23:30 +02:00
for ( AudioDriverJavaScript * driver : audio_drivers ) {
memdelete ( driver ) ;
2020-06-29 18:51:53 +02:00
}
2021-09-12 19:23:30 +02:00
audio_drivers . clear ( ) ;
2014-02-10 02:10:30 +01:00
}
2018-07-08 02:23:19 +02:00
// Miscellaneous
2014-02-10 02:10:30 +01:00
2021-12-16 14:00:55 +01:00
Error OS_JavaScript : : 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 ) {
2020-12-18 19:49:13 +01:00
return create_process ( p_path , p_arguments ) ;
}
2021-12-16 14:00:55 +01:00
Error OS_JavaScript : : create_process ( const String & p_path , const List < String > & p_arguments , ProcessID * r_child_id , bool p_open_console ) {
2020-05-01 14:45:45 +02:00
Array args ;
2021-07-16 05:45:57 +02:00
for ( const String & E : p_arguments ) {
args . push_back ( E ) ;
2020-05-01 14:45:45 +02:00
}
2021-07-07 16:31:06 +02:00
String json_args = Variant ( args ) . to_json_string ( ) ;
2020-10-23 18:33:20 +02:00
int failed = godot_js_os_execute ( json_args . utf8 ( ) . get_data ( ) ) ;
2020-12-18 19:49:13 +01:00
ERR_FAIL_COND_V_MSG ( failed , ERR_UNAVAILABLE , " OS::execute() or create_process() must be implemented in JavaScript via 'engine.setOnExecute' if required. " ) ;
2020-05-01 14:45:45 +02:00
return OK ;
2018-10-29 21:15:15 +01:00
}
Error OS_JavaScript : : kill ( const ProcessID & p_pid ) {
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( ERR_UNAVAILABLE , " OS::kill() is not available on the HTML5 platform. " ) ;
2018-10-29 21:15:15 +01:00
}
int OS_JavaScript : : get_process_id ( ) const {
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( 0 , " OS::get_process_id() is not available on the HTML5 platform. " ) ;
2018-10-29 21:15:15 +01:00
}
2021-08-13 05:36:23 +02:00
bool OS_JavaScript : : is_process_running ( const ProcessID & p_pid ) const {
return false ;
}
2021-02-15 15:48:06 +01:00
int OS_JavaScript : : get_processor_count ( ) const {
return godot_js_os_hw_concurrency_get ( ) ;
}
2018-07-08 02:23:19 +02:00
bool OS_JavaScript : : _check_internal_feature_support ( const String & p_feature ) {
2021-03-26 15:33:36 +01:00
if ( p_feature = = " html5 " | | p_feature = = " web " ) {
2018-07-08 02:23:19 +02:00
return true ;
2020-12-03 17:15:14 +01:00
}
2018-07-08 02:23:19 +02:00
# ifdef JAVASCRIPT_EVAL_ENABLED
2021-03-26 15:33:36 +01:00
if ( p_feature = = " javascript " ) {
2020-12-03 17:15:14 +01:00
return true ;
}
# endif
# ifndef NO_THREADS
if ( p_feature = = " threads " ) {
2018-07-08 02:23:19 +02:00
return true ;
2020-12-03 17:15:14 +01:00
}
# endif
# if WASM_GDNATIVE
if ( p_feature = = " wasm32 " ) {
return true ;
}
2018-07-08 02:23:19 +02:00
# endif
return false ;
2014-02-10 02:10:30 +01:00
}
2016-11-18 18:52:44 +01:00
String OS_JavaScript : : get_executable_path ( ) const {
2017-03-28 03:21:21 +02:00
return OS : : get_executable_path ( ) ;
2016-11-18 18:52:44 +01:00
}
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
Error OS_JavaScript : : shell_open ( String p_uri ) {
// Open URI in a new tab, browser will deal with it by protocol.
2020-10-23 18:33:20 +02:00
godot_js_os_shell_open ( p_uri . utf8 ( ) . get_data ( ) ) ;
2018-07-08 02:23:19 +02:00
return OK ;
2015-09-12 15:54:47 +02:00
}
2014-02-10 02:10:30 +01:00
2019-05-20 19:36:24 +02:00
String OS_JavaScript : : get_name ( ) const {
2018-07-08 02:23:19 +02:00
return " HTML5 " ;
}
2016-01-21 02:23:15 +01:00
2022-07-27 14:35:01 +02:00
void OS_JavaScript : : vibrate_handheld ( int p_duration_ms ) {
godot_js_input_vibrate_handheld ( p_duration_ms ) ;
}
2018-07-08 02:23:19 +02:00
String OS_JavaScript : : get_user_data_dir ( ) const {
return " /userfs " ;
2022-02-16 13:56:32 +01:00
}
2016-01-21 02:23:15 +01:00
2020-05-01 14:45:45 +02:00
String OS_JavaScript : : get_cache_path ( ) const {
return " /home/web_user/.cache " ;
}
String OS_JavaScript : : get_config_path ( ) const {
return " /home/web_user/.config " ;
}
String OS_JavaScript : : get_data_path ( ) const {
return " /home/web_user/.local/share " ;
2016-01-21 02:23:15 +01:00
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : file_access_close_callback ( const String & p_file , int p_flags ) {
2020-09-18 17:25:05 +02:00
OS_JavaScript * os = OS_JavaScript : : 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 ;
2018-07-08 02:23:19 +02:00
}
}
2018-01-12 00:15:21 +01:00
2022-01-31 15:28:12 +01:00
void OS_JavaScript : : update_pwa_state_callback ( ) {
if ( OS_JavaScript : : get_singleton ( ) ) {
OS_JavaScript : : get_singleton ( ) - > pwa_is_waiting = true ;
}
if ( JavaScript : : get_singleton ( ) ) {
JavaScript : : get_singleton ( ) - > emit_signal ( " pwa_update_available " ) ;
}
}
Error OS_JavaScript : : pwa_update ( ) {
return godot_js_pwa_update ( ) ? FAILED : OK ;
}
2018-07-08 02:23:19 +02:00
bool OS_JavaScript : : is_userfs_persistent ( ) const {
return idb_available ;
2017-10-02 16:09:24 +02:00
}
2022-04-29 00:51:04 +02:00
Error OS_JavaScript : : open_dynamic_library ( const String p_path , void * & p_library_handle , bool p_also_set_library_path , String * r_resolved_path ) {
2020-12-05 00:37:41 +01:00
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 ( ) ) ;
2022-04-29 00:51:04 +02:00
if ( r_resolved_path ! = nullptr ) {
* r_resolved_path = path ;
}
2020-12-05 00:37:41 +01:00
return OK ;
}
2018-07-08 02:23:19 +02:00
OS_JavaScript * OS_JavaScript : : get_singleton ( ) {
return static_cast < OS_JavaScript * > ( OS : : get_singleton ( ) ) ;
2017-10-02 16:09:24 +02:00
}
2020-05-01 14:45:45 +02:00
void OS_JavaScript : : initialize_joypads ( ) {
}
2018-07-08 02:23:19 +02:00
2020-05-01 14:45:45 +02:00
OS_JavaScript : : OS_JavaScript ( ) {
2020-10-23 18:33:20 +02:00
char locale_ptr [ 16 ] ;
godot_js_config_locale_get ( locale_ptr , 16 ) ;
setenv ( " LANG " , locale_ptr , true ) ;
2022-01-31 15:28:12 +01:00
godot_js_pwa_cb ( & OS_JavaScript : : update_pwa_state_callback ) ;
2020-06-29 18:51:53 +02:00
if ( AudioDriverJavaScript : : is_available ( ) ) {
2021-09-12 19:23:30 +02:00
# 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 ] ) ;
2020-06-29 18:51:53 +02:00
}
2018-01-07 00:04:09 +01:00
2020-10-23 18:33:20 +02:00
idb_available = godot_js_os_fs_is_persistent ( ) ;
2018-01-07 00:04:09 +01:00
Vector < Logger * > loggers ;
loggers . push_back ( memnew ( StdLogger ) ) ;
_set_logger ( memnew ( CompositeLogger ( loggers ) ) ) ;
2018-03-04 18:18:05 +01:00
2018-07-08 02:23:19 +02:00
FileAccessUnix : : close_notification_func = file_access_close_callback ;
2014-02-10 02:10:30 +01:00
}