Merge pull request #38587 from Faless/js/improvements
DisplayServerJavaScript, improvements to HTML5 build (still dummy renderer).
This commit is contained in:
commit
39da96e4d6
18
.travis.yml
18
.travis.yml
@ -100,15 +100,15 @@ matrix:
|
||||
packages:
|
||||
- *linux_deps
|
||||
|
||||
# - name: Javascript export template (release, emscripten latest)
|
||||
# stage: build
|
||||
# env: PLATFORM=javascript TOOLS=no TARGET=release CACHE_NAME=${PLATFORM}-emcc-latest EXTRA_ARGS="use_closure_compiler=yes"
|
||||
# os: linux
|
||||
# compiler: clang
|
||||
# addons:
|
||||
# apt:
|
||||
# packages:
|
||||
# - *linux_deps
|
||||
- name: Javascript export template (release, emscripten latest)
|
||||
stage: build
|
||||
env: PLATFORM=javascript TOOLS=no TARGET=release CACHE_NAME=${PLATFORM}-emcc-latest EXTRA_ARGS="use_closure_compiler=yes"
|
||||
os: linux
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- *linux_deps
|
||||
|
||||
before_install:
|
||||
- eval "${MATRIX_EVAL}"
|
||||
|
@ -78,10 +78,11 @@ public:
|
||||
#endif
|
||||
|
||||
void environment_set_glow(RID p_env, bool p_enable, int p_level_flags, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) {}
|
||||
|
||||
virtual void environment_glow_set_use_bicubic_upscale(bool p_enable) {}
|
||||
void environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) {}
|
||||
|
||||
void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance, bool p_roughness) {}
|
||||
void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) {}
|
||||
virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) {}
|
||||
virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_bias, float p_light_affect, float p_ao_channel_affect, RS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) {}
|
||||
virtual void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size) {}
|
||||
|
||||
@ -105,9 +106,12 @@ public:
|
||||
virtual void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) {}
|
||||
virtual void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) {}
|
||||
|
||||
virtual void shadows_quality_set(RS::ShadowQuality p_quality) {}
|
||||
virtual void directional_shadow_quality_set(RS::ShadowQuality p_quality) {}
|
||||
|
||||
RID light_instance_create(RID p_light) { return RID(); }
|
||||
void light_instance_set_transform(RID p_light_instance, const Transform &p_transform) {}
|
||||
void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_bias_scale = 1.0) {}
|
||||
void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) {}
|
||||
void light_instance_mark_visible(RID p_light_instance) {}
|
||||
|
||||
RID reflection_atlas_create() { return RID(); }
|
||||
@ -121,13 +125,16 @@ public:
|
||||
bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) { return false; }
|
||||
bool reflection_probe_instance_postprocess_step(RID p_instance) { return true; }
|
||||
|
||||
virtual RID decal_instance_create(RID p_decal) { return RID(); }
|
||||
virtual void decal_instance_set_transform(RID p_decal, const Transform &p_transform) {}
|
||||
|
||||
virtual RID gi_probe_instance_create(RID p_gi_probe) { return RID(); }
|
||||
void gi_probe_instance_set_light_data(RID p_probe, RID p_base, RID p_data) {}
|
||||
void gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform) {}
|
||||
virtual bool gi_probe_needs_update(RID p_probe) const { return false; }
|
||||
virtual void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, int p_dynamic_object_count, InstanceBase **p_dynamic_objects) {}
|
||||
|
||||
virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {}
|
||||
virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {}
|
||||
void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) {}
|
||||
virtual void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) {}
|
||||
|
||||
@ -136,11 +143,14 @@ public:
|
||||
void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) {}
|
||||
|
||||
virtual RID render_buffers_create() { return RID(); }
|
||||
virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa) {}
|
||||
virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa) {}
|
||||
|
||||
virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_curve) {}
|
||||
virtual bool screen_space_roughness_limiter_is_active() const { return false; }
|
||||
|
||||
virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) {}
|
||||
virtual void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) {}
|
||||
|
||||
bool free(RID p_rid) { return true; }
|
||||
virtual void update() {}
|
||||
|
||||
@ -217,6 +227,9 @@ public:
|
||||
virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) {}
|
||||
virtual Size2 texture_size_with_proxy(RID p_proxy) { return Size2(); }
|
||||
|
||||
virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) {}
|
||||
virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) {}
|
||||
|
||||
#if 0
|
||||
RID texture_create() {
|
||||
|
||||
@ -345,6 +358,7 @@ public:
|
||||
|
||||
bool material_is_animated(RID p_material) { return false; }
|
||||
bool material_casts_shadows(RID p_material) { return false; }
|
||||
virtual void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) {}
|
||||
void material_update_dependency(RID p_material, RasterizerScene::InstanceBase *p_instance) {}
|
||||
|
||||
/* MESH API */
|
||||
@ -608,6 +622,21 @@ public:
|
||||
virtual void base_update_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) {}
|
||||
virtual void skeleton_update_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) {}
|
||||
|
||||
/* DECAL API */
|
||||
|
||||
virtual RID decal_create() { return RID(); }
|
||||
virtual void decal_set_extents(RID p_decal, const Vector3 &p_extents) {}
|
||||
virtual void decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) {}
|
||||
virtual void decal_set_emission_energy(RID p_decal, float p_energy) {}
|
||||
virtual void decal_set_albedo_mix(RID p_decal, float p_mix) {}
|
||||
virtual void decal_set_modulate(RID p_decal, const Color &p_modulate) {}
|
||||
virtual void decal_set_cull_mask(RID p_decal, uint32_t p_layers) {}
|
||||
virtual void decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) {}
|
||||
virtual void decal_set_fade(RID p_decal, float p_above, float p_below) {}
|
||||
virtual void decal_set_normal_fade(RID p_decal, float p_fade) {}
|
||||
|
||||
virtual AABB decal_get_aabb(RID p_decal) const { return AABB(); }
|
||||
|
||||
/* GI PROBE API */
|
||||
|
||||
RID gi_probe_create() { return RID(); }
|
||||
@ -757,6 +786,24 @@ public:
|
||||
int particles_get_draw_passes(RID p_particles) const { return 0; }
|
||||
RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { return RID(); }
|
||||
|
||||
/* GLOBAL VARIABLES */
|
||||
|
||||
virtual void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) {}
|
||||
virtual void global_variable_remove(const StringName &p_name) {}
|
||||
virtual Vector<StringName> global_variable_get_list() const { return Vector<StringName>(); }
|
||||
|
||||
virtual void global_variable_set(const StringName &p_name, const Variant &p_value) {}
|
||||
virtual void global_variable_set_override(const StringName &p_name, const Variant &p_value) {}
|
||||
virtual Variant global_variable_get(const StringName &p_name) const { return Variant(); }
|
||||
virtual RS::GlobalVariableType global_variable_get_type(const StringName &p_name) const { return RS::GLOBAL_VAR_TYPE_MAX; }
|
||||
|
||||
virtual void global_variables_load_settings(bool p_load_textures = true) {}
|
||||
virtual void global_variables_clear() {}
|
||||
|
||||
virtual int32_t global_variables_instance_allocate(RID p_instance) { return 0; }
|
||||
virtual void global_variables_instance_free(RID p_instance) {}
|
||||
virtual void global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) {}
|
||||
|
||||
virtual bool particles_is_inactive(RID p_particles) const { return false; }
|
||||
|
||||
/* RENDER TARGET */
|
||||
@ -871,7 +918,12 @@ public:
|
||||
virtual void prepare_for_blitting_render_targets() {}
|
||||
virtual void blit_render_targets_to_screen(int p_screen, const BlitToScreen *p_render_targets, int p_amount) {}
|
||||
|
||||
void end_frame(bool p_swap_buffers) { OS::get_singleton()->swap_buffers(); }
|
||||
void end_frame(bool p_swap_buffers) {
|
||||
if (p_swap_buffers) {
|
||||
DisplayServer::get_singleton()->swap_buffers();
|
||||
}
|
||||
}
|
||||
|
||||
void finalize() {}
|
||||
|
||||
static Error is_viable() {
|
||||
|
2
misc/dist/html/full-size.html
vendored
2
misc/dist/html/full-size.html
vendored
@ -169,8 +169,6 @@ $GODOT_HEAD_INCLUDE
|
||||
var height = window.innerHeight;
|
||||
canvas.width = width * scale;
|
||||
canvas.height = height * scale;
|
||||
canvas.style.width = width + "px";
|
||||
canvas.style.height = height + "px";
|
||||
}
|
||||
animationCallbacks.push(adjustCanvasDimensions);
|
||||
adjustCanvasDimensions();
|
||||
|
@ -312,14 +312,14 @@ WebRTCDataChannelJS::WebRTCDataChannelJS(int js_id) {
|
||||
return;
|
||||
}
|
||||
var len = buffer.length*buffer.BYTES_PER_ELEMENT;
|
||||
var out = Module._malloc(len);
|
||||
Module.HEAPU8.set(buffer, out);
|
||||
var out = _malloc(len);
|
||||
HEAPU8.set(buffer, out);
|
||||
ccall("_emrtc_on_ch_message",
|
||||
"void",
|
||||
["number", "number", "number", "number"],
|
||||
[c_ptr, out, len, is_string]
|
||||
);
|
||||
Module._free(out);
|
||||
_free(out);
|
||||
}
|
||||
|
||||
}, this, js_id);
|
||||
|
@ -142,14 +142,14 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
|
||||
|
||||
}
|
||||
var len = buffer.length*buffer.BYTES_PER_ELEMENT;
|
||||
var out = Module._malloc(len);
|
||||
Module.HEAPU8.set(buffer, out);
|
||||
var out = _malloc(len);
|
||||
HEAPU8.set(buffer, out);
|
||||
ccall("_esws_on_message",
|
||||
"void",
|
||||
["number", "number", "number", "number"],
|
||||
[c_ptr, out, len, is_string]
|
||||
);
|
||||
Module._free(out);
|
||||
_free(out);
|
||||
});
|
||||
|
||||
socket.addEventListener("error", function (event) {
|
||||
|
@ -4,6 +4,7 @@ Import("env")
|
||||
|
||||
javascript_files = [
|
||||
"audio_driver_javascript.cpp",
|
||||
"display_server_javascript.cpp",
|
||||
"http_client_javascript.cpp",
|
||||
"javascript_eval.cpp",
|
||||
"javascript_main.cpp",
|
||||
@ -17,22 +18,22 @@ if env["threads_enabled"]:
|
||||
build = env.add_program(build_targets, javascript_files)
|
||||
|
||||
js_libraries = [
|
||||
"http_request.js",
|
||||
"native/http_request.js",
|
||||
]
|
||||
for lib in js_libraries:
|
||||
env.Append(LINKFLAGS=["--js-library", env.File(lib).path])
|
||||
env.Depends(build, js_libraries)
|
||||
|
||||
js_modules = [
|
||||
"id_handler.js",
|
||||
js_pre = [
|
||||
"native/id_handler.js",
|
||||
"native/utils.js",
|
||||
]
|
||||
for module in js_modules:
|
||||
env.Append(LINKFLAGS=["--pre-js", env.File(module).path])
|
||||
env.Depends(build, js_modules)
|
||||
for js in js_pre:
|
||||
env.Append(LINKFLAGS=["--pre-js", env.File(js).path])
|
||||
env.Depends(build, js_pre)
|
||||
|
||||
engine = [
|
||||
"engine/preloader.js",
|
||||
"engine/loader.js",
|
||||
"engine/utils.js",
|
||||
"engine/engine.js",
|
||||
]
|
||||
@ -47,11 +48,17 @@ wrap_list = [
|
||||
js_wrapped = env.Textfile("#bin/godot", [env.File(f) for f in wrap_list], TEXTFILESUFFIX="${PROGSUFFIX}.wrapped.js")
|
||||
|
||||
zip_dir = env.Dir("#bin/.javascript_zip")
|
||||
out_files = [zip_dir.File("godot.js"), zip_dir.File("godot.wasm"), zip_dir.File("godot.html")]
|
||||
in_files = [js_wrapped, build[1], "#misc/dist/html/full-size.html"]
|
||||
binary_name = "godot.tools" if env["tools"] else "godot"
|
||||
out_files = [
|
||||
zip_dir.File(binary_name + ".js"),
|
||||
zip_dir.File(binary_name + ".wasm"),
|
||||
zip_dir.File(binary_name + ".html"),
|
||||
]
|
||||
html_file = "#misc/dist/html/full-size.html"
|
||||
in_files = [js_wrapped, build[1], html_file]
|
||||
if env["threads_enabled"]:
|
||||
in_files.append(build[2])
|
||||
out_files.append(zip_dir.File("godot.worker.js"))
|
||||
out_files.append(zip_dir.File(binary_name + ".worker.js"))
|
||||
|
||||
zip_files = env.InstallAs(out_files, in_files)
|
||||
env.Zip(
|
||||
|
@ -190,19 +190,43 @@ void AudioDriverJavaScript::lock() {
|
||||
void AudioDriverJavaScript::unlock() {
|
||||
}
|
||||
|
||||
void AudioDriverJavaScript::finish() {
|
||||
void AudioDriverJavaScript::finish_async() {
|
||||
|
||||
// Close the context, add the operation to the async_finish list in module.
|
||||
int id = _driver_id;
|
||||
_driver_id = 0;
|
||||
|
||||
/* clang-format off */
|
||||
EM_ASM({
|
||||
var ref = Module.IDHandler.get($0);
|
||||
Module.async_finish.push(new Promise(function(accept, reject) {
|
||||
if (!ref) {
|
||||
console.log("Ref not found!", $0, Module.IDHandler);
|
||||
setTimeout(accept, 0);
|
||||
} else {
|
||||
const context = ref['context'];
|
||||
// Disconnect script and input.
|
||||
ref['script'].disconnect();
|
||||
if (ref['input'])
|
||||
ref['input'].disconnect();
|
||||
ref = null;
|
||||
context.close().then(function() {
|
||||
accept();
|
||||
}).catch(function(e) {
|
||||
accept();
|
||||
});
|
||||
}
|
||||
}));
|
||||
Module.IDHandler.remove($0);
|
||||
}, _driver_id);
|
||||
}, id);
|
||||
/* clang-format on */
|
||||
}
|
||||
|
||||
void AudioDriverJavaScript::finish() {
|
||||
if (internal_buffer) {
|
||||
memdelete_arr(internal_buffer);
|
||||
internal_buffer = nullptr;
|
||||
}
|
||||
_driver_id = 0;
|
||||
}
|
||||
|
||||
Error AudioDriverJavaScript::capture_start() {
|
||||
|
@ -56,6 +56,7 @@ public:
|
||||
virtual void lock();
|
||||
virtual void unlock();
|
||||
virtual void finish();
|
||||
void finish_async();
|
||||
|
||||
virtual Error capture_start();
|
||||
virtual Error capture_stop();
|
||||
|
@ -22,7 +22,7 @@ def get_opts():
|
||||
# eval() can be a security concern, so it can be disabled.
|
||||
BoolVariable("javascript_eval", "Enable JavaScript eval interface", True),
|
||||
BoolVariable("threads_enabled", "Enable WebAssembly Threads support (limited browser support)", False),
|
||||
BoolVariable("use_closure_compiler", "Use closure compiler to minimize Javascript code", False),
|
||||
BoolVariable("use_closure_compiler", "Use closure compiler to minimize JavaScript code", False),
|
||||
]
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ def configure(env):
|
||||
env.Append(CPPDEFINES=["DEBUG_ENABLED"])
|
||||
# Retain function names for backtraces at the cost of file size.
|
||||
env.Append(LINKFLAGS=["--profiling-funcs"])
|
||||
else: # 'debug'
|
||||
else: # "debug"
|
||||
env.Append(CPPDEFINES=["DEBUG_ENABLED"])
|
||||
env.Append(CCFLAGS=["-O1", "-g"])
|
||||
env.Append(LINKFLAGS=["-O1", "-g"])
|
||||
@ -150,7 +150,7 @@ def configure(env):
|
||||
env.Append(LIBS=["idbfs.js"])
|
||||
|
||||
env.Append(LINKFLAGS=["-s", "BINARYEN=1"])
|
||||
env.Append(LINKFLAGS=["-s", "MODULARIZE=1", "-s", 'EXPORT_NAME="Godot"'])
|
||||
env.Append(LINKFLAGS=["-s", "MODULARIZE=1", "-s", "EXPORT_NAME='Godot'"])
|
||||
|
||||
# Allow increasing memory buffer size during runtime. This is efficient
|
||||
# when using WebAssembly (in comparison to asm.js) and works well for
|
||||
@ -162,5 +162,10 @@ def configure(env):
|
||||
|
||||
env.Append(LINKFLAGS=["-s", "INVOKE_RUN=0"])
|
||||
|
||||
# callMain for manual start, FS for preloading.
|
||||
env.Append(LINKFLAGS=["-s", 'EXTRA_EXPORTED_RUNTIME_METHODS=["callMain", "FS"]'])
|
||||
# Allow use to take control of swapping WebGL buffers.
|
||||
env.Append(LINKFLAGS=["-s", "OFFSCREEN_FRAMEBUFFER=1"])
|
||||
|
||||
# callMain for manual start, FS for preloading, PATH and ERRNO_CODES for BrowserFS.
|
||||
env.Append(LINKFLAGS=["-s", "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain', 'FS', 'PATH']"])
|
||||
# Add code that allow exiting runtime.
|
||||
env.Append(LINKFLAGS=["-s", "EXIT_RUNTIME=1"])
|
||||
|
1208
platform/javascript/display_server_javascript.cpp
Normal file
1208
platform/javascript/display_server_javascript.cpp
Normal file
File diff suppressed because it is too large
Load Diff
188
platform/javascript/display_server_javascript.h
Normal file
188
platform/javascript/display_server_javascript.h
Normal file
@ -0,0 +1,188 @@
|
||||
/*************************************************************************/
|
||||
/* display_server_javascript.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef DISPLAY_SERVER_JAVASCRIPT_H
|
||||
#define DISPLAY_SERVER_JAVASCRIPT_H
|
||||
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/html5.h>
|
||||
|
||||
class DisplayServerJavaScript : public DisplayServer {
|
||||
|
||||
//int video_driver_index;
|
||||
|
||||
Vector2 windowed_size;
|
||||
|
||||
ObjectID window_attached_instance_id = {};
|
||||
|
||||
Ref<InputEventKey> deferred_key_event;
|
||||
CursorShape cursor_shape = CURSOR_ARROW;
|
||||
String cursors[CURSOR_MAX];
|
||||
Map<CursorShape, Vector<Variant>> cursors_cache;
|
||||
Point2 touches[32];
|
||||
|
||||
Point2i last_click_pos = Point2(-100, -100); // TODO check this again.
|
||||
double last_click_ms = 0;
|
||||
int last_click_button_index = -1;
|
||||
|
||||
static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data);
|
||||
|
||||
static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
|
||||
static EM_BOOL keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
|
||||
static EM_BOOL keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
|
||||
|
||||
static EM_BOOL mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
|
||||
static EM_BOOL mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
|
||||
|
||||
static EM_BOOL wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data);
|
||||
|
||||
static EM_BOOL touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data);
|
||||
static EM_BOOL touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data);
|
||||
|
||||
static EM_BOOL gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data);
|
||||
void process_joypads();
|
||||
|
||||
static Vector<String> get_rendering_drivers_func();
|
||||
static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
|
||||
|
||||
static void _dispatch_input_event(const Ref<InputEvent> &p_event);
|
||||
|
||||
protected:
|
||||
virtual int get_current_video_driver() const;
|
||||
|
||||
public:
|
||||
// Override return type to make writing static callbacks less tedious.
|
||||
static DisplayServerJavaScript *get_singleton();
|
||||
|
||||
WindowMode window_mode = WINDOW_MODE_WINDOWED;
|
||||
|
||||
String clipboard;
|
||||
String canvas_id;
|
||||
|
||||
Callable window_event_callback;
|
||||
Callable input_event_callback;
|
||||
Callable input_text_callback;
|
||||
Callable drop_files_callback;
|
||||
|
||||
// from DisplayServer
|
||||
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
|
||||
virtual bool has_feature(Feature p_feature) const;
|
||||
virtual String get_name() const;
|
||||
|
||||
// cursor
|
||||
virtual void cursor_set_shape(CursorShape p_shape);
|
||||
virtual CursorShape cursor_get_shape() const;
|
||||
virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
|
||||
|
||||
// mouse
|
||||
virtual void mouse_set_mode(MouseMode p_mode);
|
||||
virtual MouseMode mouse_get_mode() const;
|
||||
|
||||
// touch
|
||||
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
|
||||
|
||||
// clipboard
|
||||
virtual void clipboard_set(const String &p_text);
|
||||
virtual String clipboard_get() const;
|
||||
|
||||
// screen
|
||||
virtual int get_screen_count() const;
|
||||
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
|
||||
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
|
||||
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
|
||||
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
|
||||
|
||||
// windows
|
||||
virtual Vector<DisplayServer::WindowID> get_window_list() const;
|
||||
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const;
|
||||
|
||||
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID);
|
||||
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const;
|
||||
|
||||
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
|
||||
|
||||
virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
|
||||
virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
|
||||
virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
|
||||
|
||||
virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
|
||||
|
||||
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID);
|
||||
|
||||
virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const;
|
||||
virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID);
|
||||
|
||||
virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const;
|
||||
virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID);
|
||||
|
||||
virtual void window_set_transient(WindowID p_window, WindowID p_parent);
|
||||
|
||||
virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
|
||||
virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const;
|
||||
|
||||
virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
|
||||
virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const;
|
||||
|
||||
virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
|
||||
virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const;
|
||||
virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const; // FIXME: Find clearer name for this.
|
||||
|
||||
virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID);
|
||||
virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const;
|
||||
|
||||
virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const;
|
||||
|
||||
virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID);
|
||||
virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const;
|
||||
|
||||
virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID);
|
||||
virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID);
|
||||
|
||||
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const;
|
||||
|
||||
virtual bool can_any_window_draw() const;
|
||||
|
||||
// events
|
||||
virtual void process_events();
|
||||
|
||||
// icon
|
||||
virtual void set_icon(const Ref<Image> &p_icon);
|
||||
|
||||
// others
|
||||
virtual void swap_buffers();
|
||||
|
||||
static void register_javascript_driver();
|
||||
DisplayServerJavaScript(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
|
||||
~DisplayServerJavaScript();
|
||||
};
|
||||
|
||||
#endif // DISPLAY_SERVER_JAVASCRIPT_H
|
@ -1,16 +1,8 @@
|
||||
Function('return this')()['Engine'] = (function() {
|
||||
|
||||
var unloadAfterInit = true;
|
||||
var canvas = null;
|
||||
var resizeCanvasOnStart = false;
|
||||
var customLocale = 'en_US';
|
||||
var wasmExt = '.wasm';
|
||||
|
||||
var preloader = new Preloader();
|
||||
var loader = new Loader();
|
||||
var rtenv = null;
|
||||
|
||||
var executableName = '';
|
||||
var wasmExt = '.wasm';
|
||||
var unloadAfterInit = true;
|
||||
var loadPath = '';
|
||||
var loadPromise = null;
|
||||
var initPromise = null;
|
||||
@ -33,31 +25,43 @@ Function('return this')()['Engine'] = (function() {
|
||||
};
|
||||
|
||||
/** @constructor */
|
||||
function Engine() {};
|
||||
function Engine() {
|
||||
this.canvas = null;
|
||||
this.executableName = '';
|
||||
this.rtenv = null;
|
||||
this.customLocale = null;
|
||||
this.resizeCanvasOnStart = false;
|
||||
this.onExecute = null;
|
||||
this.onExit = null;
|
||||
};
|
||||
|
||||
Engine.prototype.init = /** @param {string=} basePath */ function(basePath) {
|
||||
if (initPromise) {
|
||||
return initPromise;
|
||||
}
|
||||
if (!loadPromise) {
|
||||
if (loadPromise == null) {
|
||||
if (!basePath) {
|
||||
initPromise = Promise.reject(new Error("A base path must be provided when calling `init` and the engine is not loaded."));
|
||||
return initPromise;
|
||||
}
|
||||
load(basePath);
|
||||
}
|
||||
var config = {}
|
||||
var config = {};
|
||||
if (typeof stdout === 'function')
|
||||
config.print = stdout;
|
||||
if (typeof stderr === 'function')
|
||||
config.printErr = stderr;
|
||||
initPromise = loader.init(loadPromise, loadPath, config).then(function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
rtenv = loader.env;
|
||||
var me = this;
|
||||
initPromise = new Promise(function(resolve, reject) {
|
||||
config['locateFile'] = Utils.createLocateRewrite(loadPath);
|
||||
config['instantiateWasm'] = Utils.createInstantiatePromise(loadPromise);
|
||||
Godot(config).then(function(module) {
|
||||
me.rtenv = module;
|
||||
if (unloadAfterInit) {
|
||||
loadPromise = null;
|
||||
unload();
|
||||
}
|
||||
resolve();
|
||||
config = null;
|
||||
});
|
||||
});
|
||||
return initPromise;
|
||||
@ -76,33 +80,72 @@ Function('return this')()['Engine'] = (function() {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
var me = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
return me.init().then(function() {
|
||||
if (!(canvas instanceof HTMLCanvasElement)) {
|
||||
canvas = Utils.findCanvas();
|
||||
}
|
||||
rtenv['locale'] = customLocale;
|
||||
rtenv['canvas'] = canvas;
|
||||
rtenv['thisProgram'] = executableName;
|
||||
rtenv['resizeCanvasOnStart'] = resizeCanvasOnStart;
|
||||
loader.start(preloader.preloadedFiles, args).then(function() {
|
||||
loader = null;
|
||||
initPromise = null;
|
||||
resolve();
|
||||
return me.init().then(function() {
|
||||
if (!me.rtenv) {
|
||||
return Promise.reject(new Error('The engine must be initialized before it can be started'));
|
||||
}
|
||||
|
||||
if (!(me.canvas instanceof HTMLCanvasElement)) {
|
||||
me.canvas = Utils.findCanvas();
|
||||
}
|
||||
|
||||
// Canvas can grab focus on click, or key events won't work.
|
||||
if (me.canvas.tabIndex < 0) {
|
||||
me.canvas.tabIndex = 0;
|
||||
}
|
||||
|
||||
// Disable right-click context menu.
|
||||
me.canvas.addEventListener('contextmenu', function(ev) {
|
||||
ev.preventDefault();
|
||||
}, false);
|
||||
|
||||
// Until context restoration is implemented warn the user of context loss.
|
||||
me.canvas.addEventListener('webglcontextlost', function(ev) {
|
||||
alert("WebGL context lost, please reload the page");
|
||||
ev.preventDefault();
|
||||
}, false);
|
||||
|
||||
// Browser locale, or custom one if defined.
|
||||
var locale = me.customLocale;
|
||||
if (!locale) {
|
||||
locale = navigator.languages ? navigator.languages[0] : navigator.language;
|
||||
locale = locale.split('.')[0];
|
||||
}
|
||||
me.rtenv['locale'] = locale;
|
||||
me.rtenv['canvas'] = me.canvas;
|
||||
me.rtenv['thisProgram'] = me.executableName;
|
||||
me.rtenv['resizeCanvasOnStart'] = me.resizeCanvasOnStart;
|
||||
me.rtenv['noExitRuntime'] = true;
|
||||
me.rtenv['onExecute'] = me.onExecute;
|
||||
me.rtenv['onExit'] = function(code) {
|
||||
if (me.onExit)
|
||||
me.onExit(code);
|
||||
me.rtenv = null;
|
||||
}
|
||||
return new Promise(function(resolve, reject) {
|
||||
preloader.preloadedFiles.forEach(function(file) {
|
||||
me.rtenv['copyToFS'](file.path, file.buffer);
|
||||
});
|
||||
preloader.preloadedFiles.length = 0; // Clear memory
|
||||
me.rtenv['callMain'](args);
|
||||
initPromise = null;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Engine.prototype.startGame = function(execName, mainPack) {
|
||||
Engine.prototype.startGame = function(execName, mainPack, extraArgs) {
|
||||
// Start and init with execName as loadPath if not inited.
|
||||
executableName = execName;
|
||||
this.executableName = execName;
|
||||
var me = this;
|
||||
return Promise.all([
|
||||
this.init(execName),
|
||||
this.preloadFile(mainPack, mainPack)
|
||||
]).then(function() {
|
||||
return me.start('--main-pack', mainPack);
|
||||
var args = ['--main-pack', mainPack];
|
||||
if (extraArgs)
|
||||
args = args.concat(extraArgs);
|
||||
return me.start.apply(me, args);
|
||||
});
|
||||
};
|
||||
|
||||
@ -118,67 +161,85 @@ Function('return this')()['Engine'] = (function() {
|
||||
};
|
||||
|
||||
Engine.prototype.setCanvas = function(canvasElem) {
|
||||
canvas = canvasElem;
|
||||
this.canvas = canvasElem;
|
||||
};
|
||||
|
||||
Engine.prototype.setCanvasResizedOnStart = function(enabled) {
|
||||
resizeCanvasOnStart = enabled;
|
||||
this.resizeCanvasOnStart = enabled;
|
||||
};
|
||||
|
||||
Engine.prototype.setLocale = function(locale) {
|
||||
customLocale = locale;
|
||||
this.customLocale = locale;
|
||||
};
|
||||
|
||||
Engine.prototype.setExecutableName = function(newName) {
|
||||
executableName = newName;
|
||||
this.executableName = newName;
|
||||
};
|
||||
|
||||
Engine.prototype.setProgressFunc = function(func) {
|
||||
progressFunc = func;
|
||||
}
|
||||
};
|
||||
|
||||
Engine.prototype.setStdoutFunc = function(func) {
|
||||
|
||||
var print = function(text) {
|
||||
if (arguments.length > 1) {
|
||||
text = Array.prototype.slice.call(arguments).join(" ");
|
||||
}
|
||||
func(text);
|
||||
};
|
||||
if (rtenv)
|
||||
rtenv.print = print;
|
||||
if (this.rtenv)
|
||||
this.rtenv.print = print;
|
||||
stdout = print;
|
||||
};
|
||||
|
||||
Engine.prototype.setStderrFunc = function(func) {
|
||||
|
||||
var printErr = function(text) {
|
||||
if (arguments.length > 1)
|
||||
text = Array.prototype.slice.call(arguments).join(" ");
|
||||
func(text);
|
||||
};
|
||||
if (rtenv)
|
||||
rtenv.printErr = printErr;
|
||||
if (this.rtenv)
|
||||
this.rtenv.printErr = printErr;
|
||||
stderr = printErr;
|
||||
};
|
||||
|
||||
Engine.prototype.setOnExecute = function(onExecute) {
|
||||
if (this.rtenv)
|
||||
this.rtenv.onExecute = onExecute;
|
||||
this.onExecute = onExecute;
|
||||
}
|
||||
|
||||
Engine.prototype.setOnExit = function(onExit) {
|
||||
this.onExit = onExit;
|
||||
}
|
||||
|
||||
Engine.prototype.copyToFS = function(path, buffer) {
|
||||
if (this.rtenv == null) {
|
||||
throw new Error("Engine must be inited before copying files");
|
||||
}
|
||||
this.rtenv['copyToFS'](path, buffer);
|
||||
}
|
||||
|
||||
// Closure compiler exported engine methods.
|
||||
/** @export */
|
||||
Engine['isWebGLAvailable'] = Utils.isWebGLAvailable;
|
||||
Engine['load'] = load;
|
||||
Engine['unload'] = unload;
|
||||
Engine.prototype['init'] = Engine.prototype.init
|
||||
Engine.prototype['preloadFile'] = Engine.prototype.preloadFile
|
||||
Engine.prototype['start'] = Engine.prototype.start
|
||||
Engine.prototype['startGame'] = Engine.prototype.startGame
|
||||
Engine.prototype['setWebAssemblyFilenameExtension'] = Engine.prototype.setWebAssemblyFilenameExtension
|
||||
Engine.prototype['setUnloadAfterInit'] = Engine.prototype.setUnloadAfterInit
|
||||
Engine.prototype['setCanvas'] = Engine.prototype.setCanvas
|
||||
Engine.prototype['setCanvasResizedOnStart'] = Engine.prototype.setCanvasResizedOnStart
|
||||
Engine.prototype['setLocale'] = Engine.prototype.setLocale
|
||||
Engine.prototype['setExecutableName'] = Engine.prototype.setExecutableName
|
||||
Engine.prototype['setProgressFunc'] = Engine.prototype.setProgressFunc
|
||||
Engine.prototype['setStdoutFunc'] = Engine.prototype.setStdoutFunc
|
||||
Engine.prototype['setStderrFunc'] = Engine.prototype.setStderrFunc
|
||||
Engine.prototype['init'] = Engine.prototype.init;
|
||||
Engine.prototype['preloadFile'] = Engine.prototype.preloadFile;
|
||||
Engine.prototype['start'] = Engine.prototype.start;
|
||||
Engine.prototype['startGame'] = Engine.prototype.startGame;
|
||||
Engine.prototype['setWebAssemblyFilenameExtension'] = Engine.prototype.setWebAssemblyFilenameExtension;
|
||||
Engine.prototype['setUnloadAfterInit'] = Engine.prototype.setUnloadAfterInit;
|
||||
Engine.prototype['setCanvas'] = Engine.prototype.setCanvas;
|
||||
Engine.prototype['setCanvasResizedOnStart'] = Engine.prototype.setCanvasResizedOnStart;
|
||||
Engine.prototype['setLocale'] = Engine.prototype.setLocale;
|
||||
Engine.prototype['setExecutableName'] = Engine.prototype.setExecutableName;
|
||||
Engine.prototype['setProgressFunc'] = Engine.prototype.setProgressFunc;
|
||||
Engine.prototype['setStdoutFunc'] = Engine.prototype.setStdoutFunc;
|
||||
Engine.prototype['setStderrFunc'] = Engine.prototype.setStderrFunc;
|
||||
Engine.prototype['setOnExecute'] = Engine.prototype.setOnExecute;
|
||||
Engine.prototype['setOnExit'] = Engine.prototype.setOnExit;
|
||||
Engine.prototype['copyToFS'] = Engine.prototype.copyToFS;
|
||||
return Engine;
|
||||
})();
|
||||
|
@ -1,33 +0,0 @@
|
||||
var Loader = /** @constructor */ function() {
|
||||
|
||||
this.env = null;
|
||||
|
||||
this.init = function(loadPromise, basePath, config) {
|
||||
var me = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
var cfg = config || {};
|
||||
cfg['locateFile'] = Utils.createLocateRewrite(basePath);
|
||||
cfg['instantiateWasm'] = Utils.createInstantiatePromise(loadPromise);
|
||||
loadPromise = null;
|
||||
Godot(cfg).then(function(module) {
|
||||
me.env = module;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.start = function(preloadedFiles, args) {
|
||||
var me = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (!me.env) {
|
||||
reject(new Error('The engine must be initialized before it can be started'));
|
||||
}
|
||||
preloadedFiles.forEach(function(file) {
|
||||
Utils.copyToFS(me.env['FS'], file.path, file.buffer);
|
||||
});
|
||||
preloadedFiles.length = 0; // Clear memory
|
||||
me.env['callMain'](args);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
};
|
@ -27,24 +27,6 @@ var Utils = {
|
||||
return instantiateWasm;
|
||||
},
|
||||
|
||||
copyToFS: function(fs, path, buffer) {
|
||||
var p = path.lastIndexOf("/");
|
||||
var dir = "/";
|
||||
if (p > 0) {
|
||||
dir = path.slice(0, path.lastIndexOf("/"));
|
||||
}
|
||||
try {
|
||||
fs.stat(dir);
|
||||
} catch (e) {
|
||||
if (e.errno !== 44) { // 'ENOENT', see https://github.com/emscripten-core/emscripten/blob/master/system/lib/libc/musl/arch/emscripten/bits/errno.h
|
||||
throw e;
|
||||
}
|
||||
fs['mkdirTree'](dir);
|
||||
}
|
||||
// With memory growth, canOwn should be false.
|
||||
fs['writeFile'](path, new Uint8Array(buffer), {'flags': 'wx+'});
|
||||
},
|
||||
|
||||
findCanvas: function() {
|
||||
var nodes = document.getElementsByTagName('canvas');
|
||||
if (nodes.length && nodes[0] instanceof HTMLCanvasElement) {
|
||||
|
@ -34,22 +34,63 @@
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
static OS_JavaScript *os = nullptr;
|
||||
|
||||
void exit_callback() {
|
||||
emscripten_cancel_main_loop(); // After this, we can exit!
|
||||
Main::cleanup();
|
||||
int exit_code = OS_JavaScript::get_singleton()->get_exit_code();
|
||||
memdelete(os);
|
||||
os = nullptr;
|
||||
emscripten_force_exit(exit_code); // No matter that we call cancel_main_loop, regular "exit" will not work, forcing.
|
||||
}
|
||||
|
||||
void main_loop_callback() {
|
||||
|
||||
if (os->main_loop_iterate()) {
|
||||
emscripten_cancel_main_loop(); // Cancel current loop and wait for finalize_async.
|
||||
EM_ASM({
|
||||
// This will contain the list of operations that need to complete before cleanup.
|
||||
Module.async_finish = [];
|
||||
});
|
||||
os->get_main_loop()->finish();
|
||||
os->finalize_async(); // Will add all the async finish functions.
|
||||
EM_ASM({
|
||||
Promise.all(Module.async_finish).then(function() {
|
||||
Module.async_finish = [];
|
||||
ccall("cleanup_after_sync", null, []);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
String idbfs_err = String::utf8(p_idbfs_err);
|
||||
if (!idbfs_err.empty()) {
|
||||
print_line("IndexedDB not available: " + idbfs_err);
|
||||
}
|
||||
OS_JavaScript *os = OS_JavaScript::get_singleton();
|
||||
os->set_idb_available(idbfs_err.empty());
|
||||
// TODO: Check error return value.
|
||||
Main::setup2(); // Manual second phase.
|
||||
// Ease up compatibility.
|
||||
ResourceLoader::set_abort_on_missing_resources(false);
|
||||
Main::start();
|
||||
os->run_async();
|
||||
os->get_main_loop()->init();
|
||||
emscripten_resume_main_loop();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
os = new OS_JavaScript();
|
||||
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 */
|
||||
@ -59,13 +100,10 @@ int main(int argc, char *argv[]) {
|
||||
FS.syncfs(true, function(err) {
|
||||
ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""])
|
||||
});
|
||||
|
||||
);
|
||||
/* clang-format on */
|
||||
|
||||
new OS_JavaScript(argc, argv);
|
||||
// TODO: Check error return value.
|
||||
Main::setup(argv[0], argc - 1, &argv[1]);
|
||||
|
||||
return 0;
|
||||
// Continued async in main_after_fs_sync() from the syncfs() callback.
|
||||
}
|
||||
|
204
platform/javascript/native/utils.js
Normal file
204
platform/javascript/native/utils.js
Normal file
@ -0,0 +1,204 @@
|
||||
/*************************************************************************/
|
||||
/* utils.js */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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. */
|
||||
/*************************************************************************/
|
||||
|
||||
Module['copyToFS'] = function(path, buffer) {
|
||||
var p = path.lastIndexOf("/");
|
||||
var dir = "/";
|
||||
if (p > 0) {
|
||||
dir = path.slice(0, path.lastIndexOf("/"));
|
||||
}
|
||||
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
|
||||
throw e;
|
||||
}
|
||||
FS.mkdirTree(dir);
|
||||
}
|
||||
// With memory growth, canOwn should be false.
|
||||
FS.writeFile(path, new Uint8Array(buffer), {'flags': 'wx+'});
|
||||
}
|
||||
|
||||
Module.drop_handler = (function() {
|
||||
var upload = [];
|
||||
var uploadPromises = [];
|
||||
var uploadCallback = null;
|
||||
|
||||
function readFilePromise(entry, path) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
entry.file(function(file) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
var f = {
|
||||
"path": file.relativePath || file.webkitRelativePath,
|
||||
"name": file.name,
|
||||
"type": file.type,
|
||||
"size": file.size,
|
||||
"data": reader.result
|
||||
};
|
||||
if (!f['path'])
|
||||
f['path'] = f['name'];
|
||||
upload.push(f);
|
||||
resolve()
|
||||
};
|
||||
reader.onerror = function() {
|
||||
console.log("Error reading file");
|
||||
reject();
|
||||
}
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
|
||||
}, function(err) {
|
||||
console.log("Error!");
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function readDirectoryPromise(entry) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var reader = entry.createReader();
|
||||
reader.readEntries(function(entries) {
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
var ent = entries[i];
|
||||
if (ent.isDirectory) {
|
||||
uploadPromises.push(readDirectoryPromise(ent));
|
||||
} else if (ent.isFile) {
|
||||
uploadPromises.push(readFilePromise(ent));
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function processUploadsPromises(resolve, reject) {
|
||||
if (uploadPromises.length == 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
uploadPromises.pop().then(function() {
|
||||
setTimeout(function() {
|
||||
processUploadsPromises(resolve, reject);
|
||||
//processUploadsPromises.bind(null, resolve, reject)
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function dropFiles(files) {
|
||||
var args = files || [];
|
||||
var argc = args.length;
|
||||
var argv = stackAlloc((argc + 1) * 4);
|
||||
for (var i = 0; i < argc; i++) {
|
||||
HEAP32[(argv >> 2) + i] = allocateUTF8OnStack(args[i]);
|
||||
}
|
||||
HEAP32[(argv >> 2) + argc] = 0;
|
||||
// Defined in display_server_javascript.cpp
|
||||
ccall('_drop_files_callback', 'void', ['number', 'number'], [argv, argc]);
|
||||
}
|
||||
|
||||
return function(ev) {
|
||||
ev.preventDefault();
|
||||
if (ev.dataTransfer.items) {
|
||||
// Use DataTransferItemList interface to access the file(s)
|
||||
for (var i = 0; i < ev.dataTransfer.items.length; i++) {
|
||||
const item = ev.dataTransfer.items[i];
|
||||
var entry = null;
|
||||
if ("getAsEntry" in item) {
|
||||
entry = item.getAsEntry();
|
||||
} else if ("webkitGetAsEntry" in item) {
|
||||
entry = item.webkitGetAsEntry();
|
||||
}
|
||||
if (!entry) {
|
||||
console.error("File upload not supported");
|
||||
} else if (entry.isDirectory) {
|
||||
uploadPromises.push(readDirectoryPromise(entry));
|
||||
} else if (entry.isFile) {
|
||||
uploadPromises.push(readFilePromise(entry));
|
||||
} else {
|
||||
console.error("Unrecognized entry...", entry);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error("File upload not supported");
|
||||
}
|
||||
uploadCallback = new Promise(processUploadsPromises).then(function() {
|
||||
const DROP = "/tmp/drop-" + parseInt(Math.random() * Math.pow(2, 31)) + "/";
|
||||
var drops = [];
|
||||
var files = [];
|
||||
upload.forEach((elem) => {
|
||||
var path = elem['path'];
|
||||
Module['copyToFS'](DROP + path, elem['data']);
|
||||
var idx = path.indexOf("/");
|
||||
if (idx == -1) {
|
||||
// Root file
|
||||
drops.push(DROP + path);
|
||||
} else {
|
||||
// Subdir
|
||||
var sub = path.substr(0, idx);
|
||||
idx = sub.indexOf("/");
|
||||
if (idx < 0 && drops.indexOf(DROP + sub) == -1) {
|
||||
drops.push(DROP + sub);
|
||||
}
|
||||
}
|
||||
files.push(DROP + path);
|
||||
});
|
||||
uploadPromises = [];
|
||||
upload = [];
|
||||
dropFiles(drops);
|
||||
var dirs = [DROP.substr(0, DROP.length -1)];
|
||||
files.forEach(function (file) {
|
||||
FS.unlink(file);
|
||||
var dir = file.replace(DROP, "");
|
||||
var idx = dir.lastIndexOf("/");
|
||||
while (idx > 0) {
|
||||
dir = dir.substr(0, idx);
|
||||
if (dirs.indexOf(DROP + dir) == -1) {
|
||||
dirs.push(DROP + dir);
|
||||
}
|
||||
idx = dir.lastIndexOf("/");
|
||||
}
|
||||
});
|
||||
// Remove dirs.
|
||||
dirs = dirs.sort(function(a, b) {
|
||||
var al = (a.match(/\//g) || []).length;
|
||||
var bl = (b.match(/\//g) || []).length;
|
||||
if (al > bl)
|
||||
return -1;
|
||||
else if (al < bl)
|
||||
return 1;
|
||||
return 0;
|
||||
});
|
||||
dirs.forEach(function(dir) {
|
||||
FS.rmdir(dir);
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
File diff suppressed because it is too large
Load Diff
@ -35,64 +35,25 @@
|
||||
#include "core/input/input.h"
|
||||
#include "drivers/unix/os_unix.h"
|
||||
#include "servers/audio_server.h"
|
||||
#include "servers/rendering/rasterizer.h"
|
||||
|
||||
#include <emscripten/html5.h>
|
||||
|
||||
class OS_JavaScript : public OS_Unix {
|
||||
|
||||
VideoMode video_mode;
|
||||
Vector2 windowed_size;
|
||||
bool window_maximized;
|
||||
bool entering_fullscreen;
|
||||
bool just_exited_fullscreen;
|
||||
bool transparency_enabled;
|
||||
|
||||
InputDefault *input;
|
||||
Ref<InputEventKey> deferred_key_event;
|
||||
CursorShape cursor_shape;
|
||||
String cursors[CURSOR_MAX];
|
||||
Map<CursorShape, Vector<Variant>> cursors_cache;
|
||||
Point2 touches[32];
|
||||
|
||||
Point2i last_click_pos;
|
||||
double last_click_ms;
|
||||
int last_click_button_index;
|
||||
|
||||
MainLoop *main_loop;
|
||||
int video_driver_index;
|
||||
MainLoop *main_loop = nullptr;
|
||||
AudioDriverJavaScript audio_driver_javascript;
|
||||
|
||||
bool idb_available;
|
||||
int64_t sync_wait_time;
|
||||
int64_t last_sync_check_time;
|
||||
|
||||
static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data);
|
||||
|
||||
static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
|
||||
static EM_BOOL keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
|
||||
static EM_BOOL keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
|
||||
|
||||
static EM_BOOL mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
|
||||
static EM_BOOL mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
|
||||
|
||||
static EM_BOOL wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data);
|
||||
|
||||
static EM_BOOL touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data);
|
||||
static EM_BOOL touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data);
|
||||
|
||||
static EM_BOOL gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data);
|
||||
void process_joypads();
|
||||
bool finalizing = false;
|
||||
bool idb_available = false;
|
||||
int64_t sync_wait_time = -1;
|
||||
int64_t last_sync_check_time = -1;
|
||||
|
||||
static void main_loop_callback();
|
||||
|
||||
static void file_access_close_callback(const String &p_file, int p_flags);
|
||||
|
||||
protected:
|
||||
virtual int get_current_video_driver() const;
|
||||
|
||||
virtual void initialize_core();
|
||||
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
|
||||
virtual void initialize();
|
||||
|
||||
virtual void set_main_loop(MainLoop *p_main_loop);
|
||||
virtual void delete_main_loop();
|
||||
@ -105,65 +66,38 @@ public:
|
||||
// Override return type to make writing static callbacks less tedious.
|
||||
static OS_JavaScript *get_singleton();
|
||||
|
||||
virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0);
|
||||
virtual VideoMode get_video_mode(int p_screen = 0) const;
|
||||
virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const;
|
||||
|
||||
virtual void set_window_size(const Size2);
|
||||
virtual Size2 get_window_size() const;
|
||||
virtual void set_window_maximized(bool p_enabled);
|
||||
virtual bool is_window_maximized() const;
|
||||
virtual void set_window_fullscreen(bool p_enabled);
|
||||
virtual bool is_window_fullscreen() const;
|
||||
virtual Size2 get_screen_size(int p_screen = -1) const;
|
||||
|
||||
virtual Point2 get_mouse_position() const;
|
||||
virtual int get_mouse_button_state() const;
|
||||
virtual void set_cursor_shape(CursorShape p_shape);
|
||||
virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
|
||||
virtual void set_mouse_mode(MouseMode p_mode);
|
||||
virtual MouseMode get_mouse_mode() const;
|
||||
|
||||
virtual bool get_window_per_pixel_transparency_enabled() const;
|
||||
virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
|
||||
virtual void initialize_joypads();
|
||||
|
||||
virtual bool has_touchscreen_ui_hint() const;
|
||||
|
||||
virtual bool is_joy_known(int p_device);
|
||||
virtual String get_joy_guid(int p_device) const;
|
||||
|
||||
virtual int get_video_driver_count() const;
|
||||
virtual const char *get_video_driver_name(int p_driver) const;
|
||||
|
||||
virtual int get_audio_driver_count() const;
|
||||
virtual const char *get_audio_driver_name(int p_driver) const;
|
||||
|
||||
virtual void set_clipboard(const String &p_text);
|
||||
virtual String get_clipboard() const;
|
||||
|
||||
virtual MainLoop *get_main_loop() const;
|
||||
void run_async();
|
||||
void finalize_async();
|
||||
bool main_loop_iterate();
|
||||
|
||||
virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr);
|
||||
virtual Error kill(const ProcessID &p_pid);
|
||||
virtual int get_process_id() const;
|
||||
|
||||
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
|
||||
virtual void set_window_title(const String &p_title);
|
||||
virtual void set_icon(const Ref<Image> &p_icon);
|
||||
String get_executable_path() const;
|
||||
virtual Error shell_open(String p_uri);
|
||||
virtual String get_name() const;
|
||||
virtual bool can_draw() const;
|
||||
|
||||
virtual String get_resource_dir() const;
|
||||
virtual String get_cache_path() const;
|
||||
virtual String get_config_path() const;
|
||||
virtual String get_data_path() const;
|
||||
virtual String get_user_data_dir() const;
|
||||
|
||||
void set_idb_available(bool p_idb_available);
|
||||
virtual bool is_userfs_persistent() const;
|
||||
|
||||
OS_JavaScript(int p_argc, char *p_argv[]);
|
||||
void resume_audio();
|
||||
bool is_finalizing() { return finalizing; }
|
||||
|
||||
OS_JavaScript();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user