diff options
Diffstat (limited to 'platform/javascript')
-rw-r--r-- | platform/javascript/SCsub | 41 | ||||
-rw-r--r-- | platform/javascript/audio_driver_javascript.cpp | 1 | ||||
-rw-r--r-- | platform/javascript/audio_driver_javascript.h | 1 | ||||
-rw-r--r-- | platform/javascript/audio_server_javascript.cpp | 1 | ||||
-rw-r--r-- | platform/javascript/audio_server_javascript.h | 1 | ||||
-rw-r--r-- | platform/javascript/detect.py | 62 | ||||
-rw-r--r-- | platform/javascript/dom_keys.h | 3 | ||||
-rw-r--r-- | platform/javascript/export/export.cpp | 488 | ||||
-rw-r--r-- | platform/javascript/export/export.h | 1 | ||||
-rw-r--r-- | platform/javascript/godot_shell.html | 203 | ||||
-rw-r--r-- | platform/javascript/javascript_eval.cpp | 1 | ||||
-rw-r--r-- | platform/javascript/javascript_eval.h | 1 | ||||
-rw-r--r-- | platform/javascript/javascript_main.cpp | 154 | ||||
-rw-r--r-- | platform/javascript/os_javascript.cpp | 796 | ||||
-rw-r--r-- | platform/javascript/os_javascript.h | 40 | ||||
-rw-r--r-- | platform/javascript/platform_config.h | 1 | ||||
-rw-r--r-- | platform/javascript/power_javascript.cpp | 1 | ||||
-rw-r--r-- | platform/javascript/power_javascript.h | 1 | ||||
-rw-r--r-- | platform/javascript/run_icon.png | bin | 0 -> 471 bytes |
19 files changed, 864 insertions, 933 deletions
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index 02ff2090f9..b804863ee1 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -19,29 +19,34 @@ javascript_objects = [] for x in javascript_files: javascript_objects.append(env_javascript.Object(x)) -env.Append(LINKFLAGS=["-s", "EXPORTED_FUNCTIONS=\"['_main','_audio_server_mix_function','_main_after_fs_sync']\""]) +env.Append(LINKFLAGS=["-s", "EXPORTED_FUNCTIONS=\"['_main','_audio_server_mix_function','_main_after_fs_sync','_send_notification']\""]) env.Append(LINKFLAGS=["--shell-file", '"platform/javascript/godot_shell.html"']) -html_file = env.Program('#bin/godot', javascript_objects, PROGSUFFIX=env["PROGSUFFIX"] + ".html")[0] +# output file name without file extension +basename = "godot" + env["PROGSUFFIX"] +target_dir = env.Dir("#bin") +js_file = target_dir.File(basename + ".js") +implicit_targets = [js_file] + +zip_dir = target_dir.Dir('.javascript_zip') +zip_files = env.InstallAs([zip_dir.File("godot.js"), zip_dir.File("godotfs.js")], [js_file, "#misc/dist/html_fs/godotfs.js"]) + +if env['wasm'] == 'yes': + wasm_file = target_dir.File(basename+'.wasm') + implicit_targets.append(wasm_file) + zip_files.append(InstallAs(zip_dir.File('godot.wasm'), wasm_file)) +else: + asmjs_files = [target_dir.File(basename+'.asm.js'), target_dir.File(basename+'.html.mem')] + zip_files.append(InstallAs([zip_dir.File('godot.asm.js'), zip_dir.File('godot.mem')], asmjs_files)) + implicit_targets.extend(asmjs_files) + +# HTML file must be the first target in the list +html_file = env.Program(["#bin/godot"] + implicit_targets, javascript_objects, PROGSUFFIX=env["PROGSUFFIX"]+".html")[0] Depends(html_file, "godot_shell.html") -basename = "godot" + env["PROGSUFFIX"] # output file name without file extension # Emscripten hardcodes file names, so replace common base name with # placeholder while leaving extension; also change `.html.mem` to just `.mem` fixup_html = env.Substfile(html_file, SUBST_DICT=[(basename, '$$GODOT_BASE'), ('.html.mem', '.mem')], SUBSTFILESUFFIX='.fixup.html') -zip_dir = env.Dir('#bin/.javascript_zip') -zip_files = [] -js_file = env.SideEffect(html_file.File(basename+'.js'), html_file) -zip_files.append(env.InstallAs( - [zip_dir.File('godot.html'), zip_dir.File('godot.js'), zip_dir.File('godotfs.js')], - [fixup_html, js_file, '#misc/dist/html_fs/godotfs.js'])) - -if env['wasm'] == 'yes': - wasm_file = env.SideEffect(html_file.File(basename+'.wasm'), html_file) - zip_files.append(env.InstallAs(zip_dir.File('godot.wasm'), wasm_file)) -else: - asmjs_files = env.SideEffect([html_file.File(basename+'.asm.js'), html_file.File(basename+'.html.mem')], html_file) - zip_files.append(env.InstallAs([zip_dir.File('godot.asm.js'), zip_dir.File('godot.mem')], asmjs_files)) - -Zip('#bin/godot', zip_files, ZIPSUFFIX=env['PROGSUFFIX']+env['ZIPSUFFIX'], ZIPROOT=zip_dir) +zip_files.append(InstallAs(zip_dir.File('godot.html'), fixup_html)) +Zip('#bin/godot', zip_files, ZIPSUFFIX=env['PROGSUFFIX']+env['ZIPSUFFIX'], ZIPROOT=zip_dir, ZIPCOMSTR="Archving $SOURCES as $TARGET") diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp index 80bc7047ae..3e37ec293e 100644 --- a/platform/javascript/audio_driver_javascript.cpp +++ b/platform/javascript/audio_driver_javascript.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h index ca5dba7e5c..7ccaff0f43 100644 --- a/platform/javascript/audio_driver_javascript.h +++ b/platform/javascript/audio_driver_javascript.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ diff --git a/platform/javascript/audio_server_javascript.cpp b/platform/javascript/audio_server_javascript.cpp index 7dfd562402..f9b7890e12 100644 --- a/platform/javascript/audio_server_javascript.cpp +++ b/platform/javascript/audio_server_javascript.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ diff --git a/platform/javascript/audio_server_javascript.h b/platform/javascript/audio_server_javascript.h index 2f48e7e79e..58c240f793 100644 --- a/platform/javascript/audio_server_javascript.h +++ b/platform/javascript/audio_server_javascript.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index f82eae9ff2..68c8d1eea5 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -1,6 +1,6 @@ import os -import sys import string +import sys def is_active(): @@ -12,7 +12,8 @@ def get_name(): def can_build(): - return os.environ.has_key("EMSCRIPTEN_ROOT") + + return (os.environ.has_key("EMSCRIPTEN_ROOT")) def get_opts(): @@ -27,12 +28,12 @@ def get_flags(): return [ ('tools', 'no'), - ('module_etc1_enabled', 'no'), ('module_theora_enabled', 'no'), ] def create(env): + # remove Windows' .exe suffix return env.Clone(tools=['textfile', 'zip'], PROGSUFFIX='') @@ -45,10 +46,26 @@ def escape_target_backslashes(target, source, env, for_signature): def configure(env): - env['ENV'] = os.environ - env.Append(CPPPATH=['#platform/javascript']) + ## Build type + if (env["target"] == "release"): + env.Append(CCFLAGS=['-O3']) + env.Append(LINKFLAGS=['-O3']) + + elif (env["target"] == "release_debug"): + env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) + env.Append(LINKFLAGS=['-O2', '-s', 'ASSERTIONS=1']) + # retain function names at the cost of file size, for backtraces and profiling + env.Append(LINKFLAGS=['--profiling-funcs']) + + elif (env["target"] == "debug"): + env.Append(CCFLAGS=['-O1', '-D_DEBUG', '-g', '-DDEBUG_ENABLED']) + env.Append(LINKFLAGS=['-O1', '-g']) + + ## Compiler configuration + + env['ENV'] = os.environ env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT']) env['CC'] = 'emcc' env['CXX'] = 'em++' @@ -57,6 +74,7 @@ def configure(env): # Emscripten's ar has issues with duplicate file names, so use cc env['AR'] = 'emcc' env['ARFLAGS'] = '-o' + if (os.name == 'nt'): # use TempFileMunge on Windows since some commands get too long for # cmd.exe even with spawn_fix @@ -68,26 +86,20 @@ def configure(env): env['OBJSUFFIX'] = '.bc' env['LIBSUFFIX'] = '.bc' - if (env["target"] == "release"): - env.Append(CCFLAGS=['-O3']) - env.Append(LINKFLAGS=['-O3']) - elif (env["target"] == "release_debug"): - env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) - env.Append(LINKFLAGS=['-O2']) - # retain function names at the cost of file size, for backtraces and profiling - env.Append(LINKFLAGS=['--profiling-funcs']) - elif (env["target"] == "debug"): - env.Append(CCFLAGS=['-O1', '-D_DEBUG', '-Wall', '-g', '-DDEBUG_ENABLED']) - env.Append(LINKFLAGS=['-O1', '-g']) + ## Compile flags - # TODO: Move that to opus module's config - if("module_opus_enabled" in env and env["module_opus_enabled"] != "no"): - env.opus_fixed_point = "yes" + env.Append(CPPPATH=['#platform/javascript']) + env.Append(CPPFLAGS=['-DJAVASCRIPT_ENABLED', '-DUNIX_ENABLED', '-DPTHREAD_NO_RENAME', '-DTYPED_METHOD_BIND', '-DNO_THREADS']) + env.Append(CPPFLAGS=['-DGLES3_ENABLED']) # These flags help keep the file size down env.Append(CPPFLAGS=["-fno-exceptions", '-DNO_SAFE_CAST', '-fno-rtti']) - env.Append(CPPFLAGS=['-DJAVASCRIPT_ENABLED', '-DUNIX_ENABLED', '-DPTHREAD_NO_RENAME', '-DTYPED_METHOD_BIND', '-DNO_THREADS']) - env.Append(CPPFLAGS=['-DGLES3_ENABLED']) + + if env['javascript_eval'] == 'yes': + env.Append(CPPFLAGS=['-DJAVASCRIPT_EVAL_ENABLED']) + + ## Link flags + env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1']) if (env['wasm'] == 'yes'): @@ -101,8 +113,6 @@ def configure(env): env.Append(LINKFLAGS=['-s', 'ASM_JS=1']) env.Append(LINKFLAGS=['--separate-asm']) - if env['javascript_eval'] == 'yes': - env.Append(CPPFLAGS=['-DJAVASCRIPT_EVAL_ENABLED']) - - - import methods + # TODO: Move that to opus module's config + if("module_opus_enabled" in env and env["module_opus_enabled"] != "no"): + env.opus_fixed_point = "yes" diff --git a/platform/javascript/dom_keys.h b/platform/javascript/dom_keys.h index 53a2705577..4b8b764c45 100644 --- a/platform/javascript/dom_keys.h +++ b/platform/javascript/dom_keys.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ @@ -248,7 +249,7 @@ int dom2godot_scancode(int dom_keycode) { case DOM_VK_RETURN: case DOM_VK_ENTER: // unused according to MDN - return KEY_RETURN; + return KEY_ENTER; case DOM_VK_SHIFT: return KEY_SHIFT; case DOM_VK_CONTROL: return KEY_CONTROL; diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 4fdb6f39c8..b436d52363 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ @@ -26,399 +27,354 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "export.h" -#include "editor/editor_export.h" #include "editor/editor_node.h" -#include "editor/editor_settings.h" -#include "global_config.h" -#include "io/marshalls.h" +#include "editor_export.h" #include "io/zip_io.h" -#include "os/file_access.h" -#include "os/os.h" -#include "platform/javascript/logo.h" -#include "string.h" -#include "version.h" +#include "platform/javascript/logo.gen.h" +#include "platform/javascript/run_icon.gen.h" + +#define EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE "webassembly_release.zip" +#define EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG "webassembly_debug.zip" +#define EXPORT_TEMPLATE_ASMJS_RELEASE "javascript_release.zip" +#define EXPORT_TEMPLATE_ASMJS_DEBUG "javascript_debug.zip" -#if 0 class EditorExportPlatformJavaScript : public EditorExportPlatform { - GDCLASS( EditorExportPlatformJavaScript,EditorExportPlatform ); + GDCLASS(EditorExportPlatformJavaScript, EditorExportPlatform) + + Ref<ImageTexture> logo; + Ref<ImageTexture> run_icon; + bool runnable_when_last_polled; - String custom_release_package; - String custom_debug_package; + void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug); + void _fix_fsloader_js(Vector<uint8_t> &p_js, const String &p_pack_name, uint64_t p_pack_size); - enum PackMode { - PACK_SINGLE_FILE, - PACK_MULTIPLE_FILES +public: + enum Target { + TARGET_WEBASSEMBLY, + TARGET_ASMJS }; - void _fix_html(Vector<uint8_t>& p_html, const String& p_name, bool p_debug); + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features); - PackMode pack_mode; + virtual void get_export_options(List<ExportOption> *r_options); + virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; - bool show_run; + virtual String get_name() const; + virtual String get_os_name() const; + virtual Ref<Texture> get_logo() const; - int max_memory; - int version_code; + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; + virtual String get_binary_extension() const; + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); - String html_title; - String html_head_include; - String html_font_family; - String html_style_include; - bool html_controls_enabled; + virtual bool poll_devices(); + virtual int get_device_count() const; + virtual String get_device_name(int p_device) const { return TTR("Run in Browser"); } + virtual String get_device_info(int p_device) const { return TTR("Run exported HTML in the system's default browser."); } + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags); + virtual Ref<Texture> get_run_icon() const; - Ref<ImageTexture> logo; + virtual void get_platform_features(List<String> *r_features) { -protected: + r_features->push_back("web"); + r_features->push_back("JavaScript"); + } - bool _set(const StringName& p_name, const Variant& p_value); - bool _get(const StringName& p_name,Variant &r_ret) const; - void _get_property_list( List<PropertyInfo> *p_list) const; + EditorExportPlatformJavaScript(); +}; -public: +void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug) { - virtual String get_name() const { return "HTML5"; } - virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_BC; } - virtual Ref<Texture> get_logo() const { return logo; } + String str_template = String::utf8(reinterpret_cast<const char *>(p_html.ptr()), p_html.size()); + String str_export; + Vector<String> lines = str_template.split("\n"); + int memory_mb; + if (p_preset->get("options/target").operator int() != TARGET_ASMJS) + // WebAssembly allows memory growth, so start with a reasonable default + memory_mb = 1 << 4; + else + memory_mb = 1 << (p_preset->get("options/memory_size").operator int() + 5); - virtual bool poll_devices() { return show_run?true:false;} - virtual int get_device_count() const { return show_run?1:0; }; - virtual String get_device_name(int p_device) const { return "Run in Browser"; } - virtual String get_device_info(int p_device) const { return "Run exported HTML in the system's default browser."; } - virtual Error run(int p_device,int p_flags=0); + for (int i = 0; i < lines.size(); i++) { - virtual bool requires_password(bool p_debug) const { return false; } - virtual String get_binary_extension() const { return "html"; } - virtual Error export_project(const String& p_path,bool p_debug,int p_flags=0); + String current_line = lines[i]; + current_line = current_line.replace("$GODOT_TMEM", itos(memory_mb * 1024 * 1024)); + current_line = current_line.replace("$GODOT_BASE", p_name); + current_line = current_line.replace("$GODOT_HEAD_INCLUDE", p_preset->get("html/head_include")); + current_line = current_line.replace("$GODOT_DEBUG_ENABLED", p_debug ? "true" : "false"); + str_export += current_line + "\n"; + } - virtual bool can_export(String *r_error=NULL) const; + CharString cs = str_export.utf8(); + p_html.resize(cs.length()); + for (int i = 0; i < cs.length(); i++) { + p_html[i] = cs[i]; + } +} - EditorExportPlatformJavaScript(); - ~EditorExportPlatformJavaScript(); -}; +void EditorExportPlatformJavaScript::_fix_fsloader_js(Vector<uint8_t> &p_js, const String &p_pack_name, uint64_t p_pack_size) { -bool EditorExportPlatformJavaScript::_set(const StringName& p_name, const Variant& p_value) { - - String n=p_name; - - if (n=="custom_package/debug") - custom_debug_package=p_value; - else if (n=="custom_package/release") - custom_release_package=p_value; - else if (n=="browser/enable_run") - show_run=p_value; - else if (n=="options/memory_size") - max_memory=p_value; - else if (n=="html/title") - html_title=p_value; - else if (n=="html/head_include") - html_head_include=p_value; - else if (n=="html/font_family") - html_font_family=p_value; - else if (n=="html/style_include") - html_style_include=p_value; - else if (n=="html/controls_enabled") - html_controls_enabled=p_value; - else - return false; + String str_template = String::utf8(reinterpret_cast<const char *>(p_js.ptr()), p_js.size()); + String str_export; + Vector<String> lines = str_template.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (lines[i].find("$GODOT_PACK_NAME") != -1) { + str_export += lines[i].replace("$GODOT_PACK_NAME", p_pack_name); + } else if (lines[i].find("$GODOT_PACK_SIZE") != -1) { + str_export += lines[i].replace("$GODOT_PACK_SIZE", itos(p_pack_size)); + } else { + str_export += lines[i] + "\n"; + } + } - return true; + CharString cs = str_export.utf8(); + p_js.resize(cs.length()); + for (int i = 0; i < cs.length(); i++) { + p_js[i] = cs[i]; + } } -bool EditorExportPlatformJavaScript::_get(const StringName& p_name,Variant &r_ret) const{ - - String n=p_name; - - if (n=="custom_package/debug") - r_ret=custom_debug_package; - else if (n=="custom_package/release") - r_ret=custom_release_package; - else if (n=="browser/enable_run") - r_ret=show_run; - else if (n=="options/memory_size") - r_ret=max_memory; - else if (n=="html/title") - r_ret=html_title; - else if (n=="html/head_include") - r_ret=html_head_include; - else if (n=="html/font_family") - r_ret=html_font_family; - else if (n=="html/style_include") - r_ret=html_style_include; - else if (n=="html/controls_enabled") - r_ret=html_controls_enabled; - else - return false; +void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - return true; + if (p_preset->get("texture_format/s3tc")) { + r_features->push_back("s3tc"); + } + if (p_preset->get("texture_format/etc")) { + r_features->push_back("etc"); + } + if (p_preset->get("texture_format/etc2")) { + r_features->push_back("etc2"); + } } -void EditorExportPlatformJavaScript::_get_property_list( List<PropertyInfo> *p_list) const{ - p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE,"zip")); - p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE,"zip")); - p_list->push_back( PropertyInfo( Variant::INT, "options/memory_size",PROPERTY_HINT_ENUM,"32mb,64mb,128mb,256mb,512mb,1024mb")); - p_list->push_back( PropertyInfo( Variant::BOOL, "browser/enable_run")); - p_list->push_back( PropertyInfo( Variant::STRING, "html/title")); - p_list->push_back( PropertyInfo( Variant::STRING, "html/head_include",PROPERTY_HINT_MULTILINE_TEXT)); - p_list->push_back( PropertyInfo( Variant::STRING, "html/font_family")); - p_list->push_back( PropertyInfo( Variant::STRING, "html/style_include",PROPERTY_HINT_MULTILINE_TEXT)); - p_list->push_back( PropertyInfo( Variant::BOOL, "html/controls_enabled")); +void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "options/target", PROPERTY_HINT_ENUM, "WebAssembly,asm.js"), TARGET_WEBASSEMBLY)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "options/memory_size", PROPERTY_HINT_ENUM, "32 MB,64 MB,128 MB,256 MB,512 MB,1 GB"), 3)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), "")); + 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"), "")); +} - //p_list->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Copy,Single Exec.,Pack (.pck),Bundles (Optical)")); +bool EditorExportPlatformJavaScript::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { + if (p_option == "options/memory_size") { + return p_options["options/target"].operator int() == TARGET_ASMJS; + } + return true; } +String EditorExportPlatformJavaScript::get_name() const { -void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t>& p_html, const String& p_name, bool p_debug) { + return "HTML5"; +} +String EditorExportPlatformJavaScript::get_os_name() const { - String str; - String strnew; - str.parse_utf8((const char*)p_html.ptr(),p_html.size()); - Vector<String> lines=str.split("\n"); - for(int i=0;i<lines.size();i++) { + return "JavaScript"; +} - String current_line = lines[i]; - current_line = current_line.replace("$GODOT_TMEM",itos((1<<(max_memory+5))*1024*1024)); - current_line = current_line.replace("$GODOT_BASE",p_name); - current_line = current_line.replace("$GODOT_CANVAS_WIDTH",GlobalConfig::get_singleton()->get("display/window/width")); - current_line = current_line.replace("$GODOT_CANVAS_HEIGHT",GlobalConfig::get_singleton()->get("display/window/height")); - current_line = current_line.replace("$GODOT_HEAD_TITLE",!html_title.empty()?html_title:(String) GlobalConfig::get_singleton()->get("application/name")); - current_line = current_line.replace("$GODOT_HEAD_INCLUDE",html_head_include); - current_line = current_line.replace("$GODOT_STYLE_FONT_FAMILY",html_font_family); - current_line = current_line.replace("$GODOT_STYLE_INCLUDE",html_style_include); - current_line = current_line.replace("$GODOT_CONTROLS_ENABLED",html_controls_enabled?"true":"false"); - current_line = current_line.replace("$GODOT_DEBUG_ENABLED",p_debug?"true":"false"); - strnew += current_line+"\n"; - } +Ref<Texture> EditorExportPlatformJavaScript::get_logo() const { - CharString cs = strnew.utf8(); - p_html.resize(cs.length()); - for(int i=9;i<cs.length();i++) { - p_html[i]=cs[i]; - } + return logo; } -static void _fix_files(Vector<uint8_t>& html,uint64_t p_data_size) { +bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + r_missing_templates = false; - String str; - String strnew; - str.parse_utf8((const char*)html.ptr(),html.size()); - Vector<String> lines=str.split("\n"); - for(int i=0;i<lines.size();i++) { - if (lines[i].find("$DPLEN")!=-1) { - strnew+=lines[i].replace("$DPLEN",itos(p_data_size)); - } else { - strnew+=lines[i]+"\n"; - } - } - - CharString cs = strnew.utf8(); - html.resize(cs.length()); - for(int i=9;i<cs.length();i++) { - html[i]=cs[i]; + if (p_preset->get("options/target").operator int() == TARGET_WEBASSEMBLY) { + if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE) == String()) + r_missing_templates = true; + else if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG) == String()) + r_missing_templates = true; + } else { + if (find_export_template(EXPORT_TEMPLATE_ASMJS_RELEASE) == String()) + r_missing_templates = true; + else if (find_export_template(EXPORT_TEMPLATE_ASMJS_DEBUG) == String()) + r_missing_templates = true; } + return !r_missing_templates; } -struct JSExportData { - - EditorProgress *ep; - FileAccess *f; - -}; - +String EditorExportPlatformJavaScript::get_binary_extension() const { + return "html"; +} -Error EditorExportPlatformJavaScript::export_project(const String& p_path, bool p_debug, int p_flags) { +Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + String custom_debug = p_preset->get("custom_template/debug"); + String custom_release = p_preset->get("custom_template/release"); - String src_template; + String template_path = p_debug ? custom_debug : custom_release; - EditorProgress ep("export","Exporting for javascript",104); + template_path = template_path.strip_edges(); - if (p_debug) - src_template=custom_debug_package; - else - src_template=custom_release_package; + if (template_path == String()) { - if (src_template=="") { - String err; - if (p_debug) { - src_template=find_export_template("javascript_debug.zip", &err); + if (p_preset->get("options/target").operator int() == TARGET_WEBASSEMBLY) { + if (p_debug) + template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG); + else + template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE); } else { - src_template=find_export_template("javascript_release.zip", &err); - } - if (src_template=="") { - EditorNode::add_io_error(err); - return ERR_FILE_NOT_FOUND; + if (p_debug) + template_path = find_export_template(EXPORT_TEMPLATE_ASMJS_DEBUG); + else + template_path = find_export_template(EXPORT_TEMPLATE_ASMJS_RELEASE); } } - FileAccess *src_f=NULL; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - - ep.step("Exporting to HTML5",0); + if (template_path != String() && !FileAccess::exists(template_path)) { + EditorNode::get_singleton()->show_warning(TTR("Template file not found:\n") + template_path); + return ERR_FILE_NOT_FOUND; + } - ep.step("Finding Files..",1); + String pck_path = p_path.get_basename() + ".pck"; + Error error = save_pack(p_preset, pck_path); + if (error != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:\n") + pck_path); + return error; + } - FileAccess *f=FileAccess::open(p_path.get_base_dir()+"/data.pck",FileAccess::WRITE); + FileAccess *f = FileAccess::open(pck_path, FileAccess::READ); if (!f) { - EditorNode::add_io_error("Could not create file for writing:\n"+p_path.get_basename()+"_files.js"); - return ERR_FILE_CANT_WRITE; + EditorNode::get_singleton()->show_warning(TTR("Could not read file:\n") + pck_path); + return ERR_FILE_CANT_READ; } - Error err = save_pack(f); - size_t len = f->get_len(); + size_t pack_size = f->get_len(); memdelete(f); - if (err) - return err; + FileAccess *src_f = NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + unzFile pkg = unzOpen2(template_path.utf8().get_data(), &io); - unzFile pkg = unzOpen2(src_template.utf8().get_data(), &io); if (!pkg) { - EditorNode::add_io_error("Could not find template HTML5 to export:\n"+src_template); + EditorNode::get_singleton()->show_warning(TTR("Could not open template for export:\n") + template_path); return ERR_FILE_NOT_FOUND; } - ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN); int ret = unzGoToFirstFile(pkg); - - - while(ret==UNZ_OK) { + while (ret == UNZ_OK) { //get filename unz_file_info info; char fname[16384]; - ret = unzGetCurrentFileInfo(pkg,&info,fname,16384,NULL,0,NULL,0); + ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0); - String file=fname; + String file = fname; Vector<uint8_t> data; data.resize(info.uncompressed_size); //read unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg,data.ptr(),data.size()); + unzReadCurrentFile(pkg, data.ptr(), data.size()); unzCloseCurrentFile(pkg); //write - if (file=="godot.html") { + if (file == "godot.html") { - _fix_html(data,p_path.get_file().get_basename(), p_debug); - file=p_path.get_file(); - } - if (file=="godotfs.js") { - - _fix_files(data,len); - file=p_path.get_file().get_basename()+"fs.js"; - } - if (file=="godot.js") { + _fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug); + file = p_path.get_file(); + } else if (file == "godotfs.js") { - file=p_path.get_file().get_basename()+".js"; - } + _fix_fsloader_js(data, pck_path.get_file(), pack_size); + file = p_path.get_file().get_basename() + "fs.js"; + } else if (file == "godot.js") { - if (file=="godot.asm.js") { + file = p_path.get_file().get_basename() + ".js"; + } else if (file == "godot.wasm") { - file=p_path.get_file().get_basename()+".asm.js"; - } + file = p_path.get_file().get_basename() + ".wasm"; + } else if (file == "godot.asm.js") { - if (file=="godot.mem") { + file = p_path.get_file().get_basename() + ".asm.js"; + } else if (file == "godot.mem") { - file=p_path.get_file().get_basename()+".mem"; - } - - if (file=="godot.wasm") { - - file=p_path.get_file().get_basename()+".wasm"; + file = p_path.get_file().get_basename() + ".mem"; } String dst = p_path.get_base_dir().plus_file(file); - FileAccess *f=FileAccess::open(dst,FileAccess::WRITE); + FileAccess *f = FileAccess::open(dst, FileAccess::WRITE); if (!f) { - EditorNode::add_io_error("Could not create file for writing:\n"+dst); + EditorNode::get_singleton()->show_warning(TTR("Could not write file:\n") + dst); unzClose(pkg); return ERR_FILE_CANT_WRITE; } - f->store_buffer(data.ptr(),data.size()); + f->store_buffer(data.ptr(), data.size()); memdelete(f); - ret = unzGoToNextFile(pkg); } - - return OK; - } +bool EditorExportPlatformJavaScript::poll_devices() { -Error EditorExportPlatformJavaScript::run(int p_device, int p_flags) { + Ref<EditorExportPreset> preset; - String path = EditorSettings::get_singleton()->get_settings_path()+"/tmp/tmp_export.html"; - Error err = export_project(path,true,p_flags); - if (err) - return err; + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { - OS::get_singleton()->shell_open(path); + Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i); + if (ep->is_runnable() && ep->get_platform() == this) { + preset = ep; + break; + } + } - return OK; + bool prev = runnable_when_last_polled; + runnable_when_last_polled = preset.is_valid(); + return runnable_when_last_polled != prev; } +int EditorExportPlatformJavaScript::get_device_count() const { -EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { - - show_run=false; - Image img( _javascript_logo ); - logo = Ref<ImageTexture>( memnew( ImageTexture )); - logo->create_from_image(img); - max_memory=3; - html_title=""; - html_font_family="'Droid Sans',arial,sans-serif"; - html_controls_enabled=true; - pack_mode=PACK_SINGLE_FILE; + return runnable_when_last_polled; } -bool EditorExportPlatformJavaScript::can_export(String *r_error) const { - - - bool valid=true; - String err; +Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { - if (!exists_export_template("javascript_debug.zip") || !exists_export_template("javascript_release.zip")) { - valid=false; - err+="No export templates found.\nDownload and install export templates.\n"; - } - - if (custom_debug_package!="" && !FileAccess::exists(custom_debug_package)) { - valid=false; - err+="Custom debug package not found.\n"; - } - - if (custom_release_package!="" && !FileAccess::exists(custom_release_package)) { - valid=false; - err+="Custom release package not found.\n"; + String path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/tmp_export.html"; + Error err = export_project(p_preset, true, path, p_debug_flags); + if (err) { + return err; } + OS::get_singleton()->shell_open(path); + return OK; +} - if (r_error) - *r_error=err; +Ref<Texture> EditorExportPlatformJavaScript::get_run_icon() const { - return valid; + return run_icon; } +EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { + + Ref<Image> img = memnew(Image(_javascript_logo)); + logo.instance(); + logo->create_from_image(img); -EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() { + img = Ref<Image>(memnew(Image(_javascript_run_icon))); + run_icon.instance(); + run_icon->create_from_image(img); + runnable_when_last_polled = false; } -#endif void register_javascript_exporter() { - //Ref<EditorExportPlatformJavaScript> exporter = Ref<EditorExportPlatformJavaScript>( memnew(EditorExportPlatformJavaScript) ); - //EditorImportExport::get_singleton()->add_export_platform(exporter); + Ref<EditorExportPlatformJavaScript> platform; + platform.instance(); + EditorExport::get_singleton()->add_export_platform(platform); } diff --git a/platform/javascript/export/export.h b/platform/javascript/export/export.h index 59c0a67e6d..910c4119f7 100644 --- a/platform/javascript/export/export.h +++ b/platform/javascript/export/export.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ diff --git a/platform/javascript/godot_shell.html b/platform/javascript/godot_shell.html index 65f3b4a340..ee7399a129 100644 --- a/platform/javascript/godot_shell.html +++ b/platform/javascript/godot_shell.html @@ -2,8 +2,7 @@ <html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang=""> <head> <meta charset="utf-8" /> - <title>$GODOT_HEAD_TITLE</title> -$GODOT_HEAD_INCLUDE + <title></title> <style type="text/css"> body { margin: 0; @@ -11,7 +10,7 @@ $GODOT_HEAD_INCLUDE padding: 0; text-align: center; background-color: #222226; - font-family: $GODOT_STYLE_FONT_FAMILY; + font-family: 'Droid Sans', Arial, sans-serif; } @@ -27,7 +26,7 @@ $GODOT_HEAD_INCLUDE } button.godot { - font-family: $GODOT_STYLE_FONT_FAMILY; /* override user agent style */ + font-family: 'Droid Sans', Arial, sans-serif; /* override user agent style */ padding: 1px 5px; background-color: #37353f; background-image: linear-gradient(to bottom, #413e49, #3a3842); @@ -84,6 +83,10 @@ $GODOT_HEAD_INCLUDE color: white; } + #canvas:focus { + outline: none; + } + /* Status display * ============== */ @@ -109,53 +112,12 @@ $GODOT_HEAD_INCLUDE } - /* On-hover controls - * ================= */ - - #controls { - visibility: hidden; - opacity: 0.0; - transition: opacity 500ms ease-in-out 200ms; - position: absolute; - right: 16px; - top: 16px; - padding: 3px 5px; - font-size: small; - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - } - - :hover > #controls { - opacity: 1.0; - transition: opacity 60ms ease-in-out; - } - - #controls > button, - #controls > label { - vertical-align: middle; - margin-left: 2px; - margin-right: 2px; - } - - #controls > label > input { - vertical-align: middle; - } - - #controls > label > input[type="checkbox"] { - /* override user agent style */ - margin-left: 0; - } - - #output-toggle { display: none; } - - /* Debug output * ============ */ #output-panel { display: none; - max-width: $GODOT_CANVAS_WIDTHpx; + max-width: 700px; font-size: small; margin: 6px auto 0; padding: 0 4px 4px; @@ -184,32 +146,18 @@ $GODOT_HEAD_INCLUDE font-size: small; font-family: "Lucida Console", Monaco, monospace; } - - -/* Export style include - * ==================== */ - -$GODOT_STYLE_INCLUDE - </style> +$GODOT_HEAD_INCLUDE </head> <body> <div id="container"> - <canvas id="canvas" width="$GODOT_CANVAS_WIDTH" height="$GODOT_CANVAS_HEIGHT" onclick="canvas.ownerDocument.defaultView.focus();" oncontextmenu="event.preventDefault();"> + <canvas id="canvas" width="640" height="480" tabindex="0" oncontextmenu="event.preventDefault();"> HTML5 canvas appears to be unsupported in the current browser.<br /> Please try updating or use a different browser. </canvas> <div id="status-container"> <span id="status" class="godot" onclick="this.style.visibility='hidden';">Downloading page...</span> </div> - <div id="controls" class="godot"> - <label id="output-toggle"><input type="checkbox" checked="checked" autocomplete="off" onchange="Presentation.setOutputVisible(this.checked);" />Display Output</label> - <!-- hidden until implemented - <label><input class="postRun-enable" type="checkbox" disabled="disabled" autocomplete="off" />lock cursor</label> - <label><input class="postRun-enable" type="checkbox" disabled="disabled" autocomplete="off" onchange="Presentation.setCanvasMaximized(this.checked);" />maximize</label> - --> - <button id="fullscreen" class="godot postRun-enable" type="button" disabled="disabled" autocomplete="off" onclick="Presentation.requestFullscreen();">Fullscreen</button> - </div> </div> <div id="output-panel" class="godot"> <div id="output-header"> @@ -226,33 +174,9 @@ $GODOT_STYLE_INCLUDE var canvasElement = document.getElementById("canvas"); var presentation = { - postRun: [ - function() { - var elements = document.getElementsByClassName("postRun-enable"); - Array.prototype.slice.call(elements).forEach(function(element) { - element.disabled = false; - }); - } - ], - requestFullscreen: function requestFullscreen() { - if (typeof Module !== "undefined" && Module.requestFullscreen) { - Module.requestFullscreen(false, false); - } - }, - /* - requestPointerlock: function requestPointerlock() { - if (typeof Module !== "undefined" && Module.requestPointerlock) { - Module.requestPointerlock(false, false); - } - }, - setCanvasMaximized: function setCanvasMaximized(enabled) { - if (typeof Module !== "undefined" && Module.setCanvasMaximized) { - Module.setCanvasMaximized(enabled); - } - }, - */ + postRun: [], setStatusVisible: function setStatusVisible(visible) { - statusElement.style.visibility = (visible?"visible":"hidden"); + statusElement.style.visibility = (visible ? "visible" : "hidden"); }, setStatus: function setStatus(text) { if (text.length === 0) { @@ -288,18 +212,13 @@ $GODOT_STYLE_INCLUDE window.onerror = function(event) { presentation.setStatus("Failure during start-up\nSee JavaScript console") }; - if ($GODOT_CONTROLS_ENABLED) { // controls enabled - document.getElementById("controls").style.visibility="visible"; - } - if ($GODOT_DEBUG_ENABLED) { // debugging enabled var outputRoot = document.getElementById("output-panel"); var outputElement = document.getElementById("output-scroll"); - var outputToggle = document.getElementById("output-toggle"); const maxOutputMessages = 400; presentation.setOutputVisible = function setOutputVisible(visible) { - outputRoot.style.display = (visible?"block":"none"); + outputRoot.style.display = (visible ? "block" : "none"); }; presentation.clearOutput = function clearOutput() { while (outputElement.firstChild) { @@ -308,7 +227,6 @@ $GODOT_STYLE_INCLUDE }; presentation.setOutputVisible(true); - outputToggle.style.display = "inline"; presentation.print = function print(text) { if (arguments.length > 1) { @@ -347,56 +265,59 @@ $GODOT_STYLE_INCLUDE })(); // Emscripten interface - var Module = { - TOTAL_MEMORY: $GODOT_TMEM, - postRun: (function() { - if (typeof Presentation !== "undefined" && Presentation.postRun instanceof Array) { - return Presentation.postRun; - } - })(), - print: function print(text) { - if (arguments.length > 1) { - text = Array.prototype.slice.call(arguments).join(" "); - } - console.log(text); - if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") { - Presentation.print(text); - } - }, - printErr: function printErr(text) { - if (arguments.length > 1) { - text = Array.prototype.slice.call(arguments).join(" "); - } - console.error(text); - if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") { - Presentation.print("**ERROR**:", text) - } - }, - canvas: (function() { - var canvas = document.getElementById("canvas"); - // As a default initial behavior, pop up an alert when WebGL context is lost. To make your - // application robust, you may want to override this behavior before shipping! - // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 - canvas.addEventListener("webglcontextlost", function(e) { alert("WebGL context lost. Plase reload the page."); e.preventDefault(); }, false); - return canvas; - - })(), - setStatus: function setStatus(text) { - var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); - var now = Date.now(); - if (m) { - if (now - Date.now() < 30) // if this is a progress update, skip it if too soon - return; - text = m[1]; - } - if (typeof Presentation !== "undefined" && typeof Presentation.setStatus == "function") { - Presentation.setStatus(text); + var Module = (function() { + const BASE_NAME = '$GODOT_BASE'; + var module = { + thisProgram: BASE_NAME, + wasmBinaryFile: BASE_NAME + '.wasm', + TOTAL_MEMORY: $GODOT_TMEM, + print: function print(text) { + if (arguments.length > 1) { + text = Array.prototype.slice.call(arguments).join(" "); + } + console.log(text); + if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") { + Presentation.print(text); + } + }, + printErr: function printErr(text) { + if (arguments.length > 1) { + text = Array.prototype.slice.call(arguments).join(" "); + } + console.error(text); + if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") { + Presentation.print("**ERROR**:", text) + } + }, + canvas: document.getElementById("canvas"), + setStatus: function setStatus(text) { + var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); + var now = Date.now(); + if (m) { + if (now - Date.now() < 30) // if this is a progress update, skip it if too soon + return; + text = m[1]; + } + if (typeof Presentation !== "undefined" && typeof Presentation.setStatus == "function") { + Presentation.setStatus(text); + } } + }; + + // As a default initial behavior, pop up an alert when WebGL context is lost. To make your + // application robust, you may want to override this behavior before shipping! + // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 + module.canvas.addEventListener("webglcontextlost", function(e) { alert("WebGL context lost. Plase reload the page."); e.preventDefault(); }, false); + + if (typeof Presentation !== "undefined" && Presentation.postRun instanceof Array) { + module.postRun = Presentation.postRun; } - }; + + return module; + })(); if (!Presentation.isWebGL2Available()) { - Presentation.setStatus("WebGL2 appears to be unsupported in the current browser.\nPlease try updating or use a different browser."); + Presentation.setStatus("WebGL 2 appears to be unsupported.\nPlease update browser and drivers."); Presentation.preventLoading = true; } else { Presentation.setStatus("Downloading..."); diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp index 897c2276bb..c9312e8d30 100644 --- a/platform/javascript/javascript_eval.cpp +++ b/platform/javascript/javascript_eval.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ diff --git a/platform/javascript/javascript_eval.h b/platform/javascript/javascript_eval.h index 0050b855d8..4a732cec76 100644 --- a/platform/javascript/javascript_eval.h +++ b/platform/javascript/javascript_eval.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp index 00e531baa1..6b1d574496 100644 --- a/platform/javascript/javascript_main.cpp +++ b/platform/javascript/javascript_main.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ @@ -30,171 +31,50 @@ #include "io/resource_loader.h" #include "main/main.h" #include "os_javascript.h" -#include <GL/glut.h> -#include <string.h> OS_JavaScript *os = NULL; -static void _gfx_init(void *ud, bool gl2, int w, int h, bool fs) { +static void main_loop() { - glutInitWindowSize(w, h); - glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); - glutCreateWindow("godot"); + os->main_loop_iterate(); } -static uint32_t _mouse_button_mask = 0; +extern "C" void main_after_fs_sync() { -static void _glut_mouse_button(int button, int state, int x, int y) { - - InputEvent ev; - ev.type = InputEvent::MOUSE_BUTTON; - switch (button) { - case GLUT_LEFT_BUTTON: ev.mouse_button.button_index = BUTTON_LEFT; break; - case GLUT_MIDDLE_BUTTON: ev.mouse_button.button_index = BUTTON_MIDDLE; break; - case GLUT_RIGHT_BUTTON: ev.mouse_button.button_index = BUTTON_RIGHT; break; - case 3: ev.mouse_button.button_index = BUTTON_WHEEL_UP; break; - case 4: ev.mouse_button.button_index = BUTTON_WHEEL_DOWN; break; - } - - ev.mouse_button.pressed = state == GLUT_DOWN; - ev.mouse_button.x = x; - ev.mouse_button.y = y; - ev.mouse_button.global_x = x; - ev.mouse_button.global_y = y; - - if (ev.mouse_button.button_index < 4) { - if (ev.mouse_button.pressed) { - _mouse_button_mask |= 1 << (ev.mouse_button.button_index - 1); - } else { - _mouse_button_mask &= ~(1 << (ev.mouse_button.button_index - 1)); - } - } - ev.mouse_button.button_mask = _mouse_button_mask; - - uint32_t m = glutGetModifiers(); - ev.mouse_button.mod.alt = (m & GLUT_ACTIVE_ALT) != 0; - ev.mouse_button.mod.shift = (m & GLUT_ACTIVE_SHIFT) != 0; - ev.mouse_button.mod.control = (m & GLUT_ACTIVE_CTRL) != 0; - - os->push_input(ev); - - if (ev.mouse_button.button_index == BUTTON_WHEEL_UP || ev.mouse_button.button_index == BUTTON_WHEEL_DOWN) { - // GLUT doesn't send release events for mouse wheel, so send manually - ev.mouse_button.pressed = false; - os->push_input(ev); - } -} - -static int _glut_prev_x = 0; -static int _glut_prev_y = 0; - -static void _glut_mouse_motion(int x, int y) { - - InputEvent ev; - ev.type = InputEvent::MOUSE_MOTION; - ev.mouse_motion.button_mask = _mouse_button_mask; - ev.mouse_motion.x = x; - ev.mouse_motion.y = y; - ev.mouse_motion.global_x = x; - ev.mouse_motion.global_y = y; - ev.mouse_motion.relative_x = x - _glut_prev_x; - ev.mouse_motion.relative_y = y - _glut_prev_y; - _glut_prev_x = x; - _glut_prev_y = y; - - uint32_t m = glutGetModifiers(); - ev.mouse_motion.mod.alt = (m & GLUT_ACTIVE_ALT) != 0; - ev.mouse_motion.mod.shift = (m & GLUT_ACTIVE_SHIFT) != 0; - ev.mouse_motion.mod.control = (m & GLUT_ACTIVE_CTRL) != 0; - - os->push_input(ev); -} - -static void _gfx_idle() { - - glutPostRedisplay(); -} - -int start_step = 0; - -static void _godot_draw(void) { - - if (start_step == 1) { - start_step = 2; - Main::start(); - os->main_loop_begin(); - } - - if (start_step == 2) { - os->main_loop_iterate(); - } - - glutSwapBuffers(); -} - -extern "C" { - -void main_after_fs_sync() { - - start_step = 1; -} + // Ease up compatibility + ResourceLoader::set_abort_on_missing_resources(false); + Main::start(); + os->main_loop_begin(); + emscripten_set_main_loop(main_loop, 0, false); } int main(int argc, char *argv[]) { - /* Initialize the window */ printf("let it go dude!\n"); - glutInit(&argc, argv); - os = new OS_JavaScript(_gfx_init, NULL, NULL); -#if 0 - char *args[]={"-test","gui","-v",NULL}; - Error err = Main::setup("apk",3,args); -#else - char *args[] = { "-main_pack", "data.pck", NULL }; //pass location of main pack manually, because it wont get an executable name - Error err = Main::setup("", 2, args); - -#endif - ResourceLoader::set_abort_on_missing_resources(false); //ease up compatibility - - glutMouseFunc(_glut_mouse_button); - glutMotionFunc(_glut_mouse_motion); - glutPassiveMotionFunc(_glut_mouse_motion); - /* Set up glut callback functions */ - glutIdleFunc(_gfx_idle); - // glutReshapeFunc(gears_reshape); - glutDisplayFunc(_godot_draw); - //glutSpecialFunc(gears_special); - - //mount persistent file system + // sync from persistent state into memory and then + // run the 'main_after_fs_sync' function /* clang-format off */ EM_ASM( + Module.noExitRuntime = true; FS.mkdir('/userfs'); FS.mount(IDBFS, {}, '/userfs'); - - // sync from persistent state into memory and then - // run the 'main_after_fs_sync' function FS.syncfs(true, function(err) { - if (err) { Module.setStatus('Failed to load persistent data\nPlease allow (third-party) cookies'); Module.printErr('Failed to populate IDB file system: ' + err.message); - Module.exit(); + Module.noExitRuntime = false; } else { Module.print('Successfully populated IDB file system'); - ccall('main_after_fs_sync', 'void', []); + ccall('main_after_fs_sync', null); } }); ); /* clang-format on */ - glutMainLoop(); + os = new OS_JavaScript(argv[0], NULL); + Error err = Main::setup(argv[0], argc - 1, &argv[1]); return 0; + // continued async in main_after_fs_sync() from syncfs() callback } - -/* - * - *09] <azakai|2__> reduz: yes, define TOTAL_MEMORY on Module. for example var Module = { TOTAL_MEMORY: 12345.. }; before the main - * - */ diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 83072c30aa..d339baf024 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ @@ -28,8 +29,8 @@ /*************************************************************************/ #include "os_javascript.h" -#include "core/global_config.h" #include "core/io/file_access_buffered_fa.h" +#include "core/project_settings.h" #include "dom_keys.h" #include "drivers/gles3/rasterizer_gles3.h" #include "drivers/unix/dir_access_unix.h" @@ -40,6 +41,19 @@ #include <emscripten.h> #include <stdlib.h> +#define DOM_BUTTON_LEFT 0 +#define DOM_BUTTON_MIDDLE 1 +#define DOM_BUTTON_RIGHT 2 + +template <typename T> +static void dom2godot_mod(T emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) { + + godot_event->set_shift(emscripten_event_ptr->shiftKey); + godot_event->set_alt(emscripten_event_ptr->altKey); + godot_event->set_control(emscripten_event_ptr->ctrlKey); + godot_event->set_metakey(emscripten_event_ptr->metaKey); +} + int OS_JavaScript::get_video_driver_count() const { return 1; @@ -82,30 +96,18 @@ static EM_BOOL _browser_resize_callback(int event_type, const EmscriptenUiEvent ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_RESIZE, false); OS_JavaScript *os = static_cast<OS_JavaScript *>(user_data); - - // the order in which _browser_resize_callback and - // _fullscreen_change_callback are called is browser-dependent, - // so try adjusting for fullscreen in both - if (os->is_window_fullscreen() || os->is_window_maximized()) { - - OS::VideoMode vm = os->get_video_mode(); - vm.width = ui_event->windowInnerWidth; - vm.height = ui_event->windowInnerHeight; - os->set_video_mode(vm); - emscripten_set_canvas_size(ui_event->windowInnerWidth, ui_event->windowInnerHeight); - } + // The order of the fullscreen change event and the window size change + // event varies, even within just one browser, so defer handling + os->request_canvas_size_adjustment(); return false; } -static Size2 _windowed_size; - static EM_BOOL _fullscreen_change_callback(int event_type, const EmscriptenFullscreenChangeEvent *event, void *user_data) { ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_FULLSCREENCHANGE, false); OS_JavaScript *os = static_cast<OS_JavaScript *>(user_data); String id = String::utf8(event->id); - // empty id is canvas if (id.empty() || id == "canvas") { @@ -113,32 +115,247 @@ static EM_BOOL _fullscreen_change_callback(int event_type, const EmscriptenFulls // this event property is the only reliable information on // browser fullscreen state vm.fullscreen = event->isFullscreen; + os->set_video_mode(vm); + os->request_canvas_size_adjustment(); + } + return false; +} - if (event->isFullscreen) { - vm.width = event->screenWidth; - vm.height = event->screenHeight; - os->set_video_mode(vm); - emscripten_set_canvas_size(vm.width, vm.height); - } else { - os->set_video_mode(vm); - if (!os->is_window_maximized()) { - os->set_window_size(_windowed_size); - } +static InputDefault *_input; + +static bool is_canvas_focused() { + + /* clang-format off */ + return EM_ASM_INT_V( + return document.activeElement == Module.canvas; + ); + /* clang-format on */ +} + +static void focus_canvas() { + + /* clang-format off */ + EM_ASM( + Module.canvas.focus(); + ); + /* clang-format on */ +} + +static bool _cursor_inside_canvas = true; + +static bool is_cursor_inside_canvas() { + + return _cursor_inside_canvas; +} + +static EM_BOOL _mousebutton_callback(int event_type, const EmscriptenMouseEvent *mouse_event, void *user_data) { + + ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_MOUSEDOWN && event_type != EMSCRIPTEN_EVENT_MOUSEUP, false); + + Ref<InputEventMouseButton> ev; + ev.instance(); + ev->set_pressed(event_type == EMSCRIPTEN_EVENT_MOUSEDOWN); + ev->set_position(Point2(mouse_event->canvasX, mouse_event->canvasY)); + ev->set_global_position(ev->get_position()); + dom2godot_mod(mouse_event, ev); + + switch (mouse_event->button) { + case DOM_BUTTON_LEFT: ev->set_button_index(BUTTON_LEFT); break; + case DOM_BUTTON_MIDDLE: ev->set_button_index(BUTTON_MIDDLE); break; + case DOM_BUTTON_RIGHT: ev->set_button_index(BUTTON_RIGHT); break; + default: return false; + } + + int mask = _input->get_mouse_button_mask(); + if (ev->is_pressed()) { + // since the event is consumed, focus manually + if (!is_canvas_focused()) { + focus_canvas(); } + mask |= 1 << ev->get_button_index(); + } else if (mask & (1 << ev->get_button_index())) { + mask &= ~(1 << ev->get_button_index()); + } else { + // release event, but press was outside the canvas, so ignore + return false; } + ev->set_button_mask(mask >> 1); + + _input->parse_input_event(ev); + // prevent selection dragging + return true; +} + +static EM_BOOL _mousemove_callback(int event_type, const EmscriptenMouseEvent *mouse_event, void *user_data) { + + ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_MOUSEMOVE, false); + OS_JavaScript *os = static_cast<OS_JavaScript *>(user_data); + int input_mask = _input->get_mouse_button_mask(); + Point2 pos = Point2(mouse_event->canvasX, mouse_event->canvasY); + // outside the canvas, only read mouse movement if dragging started inside + // the canvas; imitating desktop app behaviour + if (!is_cursor_inside_canvas() && !input_mask) + return false; + + Ref<InputEventMouseMotion> ev; + ev.instance(); + dom2godot_mod(mouse_event, ev); + ev->set_button_mask(input_mask >> 1); + + ev->set_position(pos); + ev->set_global_position(ev->get_position()); + + ev->set_relative(_input->get_mouse_position() - ev->get_position()); + _input->set_mouse_position(ev->get_position()); + ev->set_speed(_input->get_last_mouse_speed()); + + _input->parse_input_event(ev); + // don't suppress mouseover/leave events return false; } -static InputEvent _setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) { +static EM_BOOL _wheel_callback(int event_type, const EmscriptenWheelEvent *wheel_event, void *user_data) { + + ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_WHEEL, false); + if (!is_canvas_focused()) { + if (is_cursor_inside_canvas()) { + focus_canvas(); + } else { + return false; + } + } + + Ref<InputEventMouseButton> ev; + ev.instance(); + ev->set_button_mask(_input->get_mouse_button_mask() >> 1); + ev->set_position(_input->get_mouse_position()); + ev->set_global_position(ev->get_position()); + + ev->set_shift(_input->is_key_pressed(KEY_SHIFT)); + ev->set_alt(_input->is_key_pressed(KEY_ALT)); + ev->set_control(_input->is_key_pressed(KEY_CONTROL)); + ev->set_metakey(_input->is_key_pressed(KEY_META)); + + if (wheel_event->deltaY < 0) + ev->set_button_index(BUTTON_WHEEL_UP); + else if (wheel_event->deltaY > 0) + ev->set_button_index(BUTTON_WHEEL_DOWN); + else if (wheel_event->deltaX > 0) + ev->set_button_index(BUTTON_WHEEL_LEFT); + else if (wheel_event->deltaX < 0) + ev->set_button_index(BUTTON_WHEEL_RIGHT); + else + return false; + + // Different browsers give wildly different delta values, and we can't + // interpret deltaMode, so use default value for wheel events' factor + + ev->set_pressed(true); + _input->parse_input_event(ev); + + ev->set_pressed(false); + _input->parse_input_event(ev); + + return true; +} + +static Point2 _prev_touches[32]; + +static EM_BOOL _touchpress_callback(int event_type, const EmscriptenTouchEvent *touch_event, void *user_data) { + + ERR_FAIL_COND_V( + event_type != EMSCRIPTEN_EVENT_TOUCHSTART && + event_type != EMSCRIPTEN_EVENT_TOUCHEND && + event_type != EMSCRIPTEN_EVENT_TOUCHCANCEL, + false); + + Ref<InputEventScreenTouch> ev; + ev.instance(); + int lowest_id_index = -1; + for (int i = 0; i < touch_event->numTouches; ++i) { + + const EmscriptenTouchPoint &touch = touch_event->touches[i]; + if (lowest_id_index == -1 || touch.identifier < touch_event->touches[lowest_id_index].identifier) + lowest_id_index = i; + if (!touch.isChanged) + continue; + ev->set_index(touch.identifier); + ev->set_position(Point2(touch.canvasX, touch.canvasY)); + _prev_touches[i] = ev->get_position(); + ev->set_pressed(event_type == EMSCRIPTEN_EVENT_TOUCHSTART); + + _input->parse_input_event(ev); + } + + if (touch_event->touches[lowest_id_index].isChanged) { + + Ref<InputEventMouseButton> ev_mouse; + ev_mouse.instance(); + ev_mouse->set_button_mask(_input->get_mouse_button_mask() >> 1); + dom2godot_mod(touch_event, ev_mouse); + + const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index]; + ev_mouse->set_position(Point2(first_touch.canvasX, first_touch.canvasY)); + ev_mouse->set_global_position(ev_mouse->get_position()); + + ev_mouse->set_button_index(BUTTON_LEFT); + ev_mouse->set_pressed(event_type == EMSCRIPTEN_EVENT_TOUCHSTART); + + _input->parse_input_event(ev_mouse); + } + return true; +} + +static EM_BOOL _touchmove_callback(int event_type, const EmscriptenTouchEvent *touch_event, void *user_data) { + + ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_TOUCHMOVE, false); + + Ref<InputEventScreenDrag> ev; + ev.instance(); + int lowest_id_index = -1; + for (int i = 0; i < touch_event->numTouches; ++i) { + + const EmscriptenTouchPoint &touch = touch_event->touches[i]; + if (lowest_id_index == -1 || touch.identifier < touch_event->touches[lowest_id_index].identifier) + lowest_id_index = i; + if (!touch.isChanged) + continue; + ev->set_index(touch.identifier); + ev->set_position(Point2(touch.canvasX, touch.canvasY)); + Point2 &prev = _prev_touches[i]; + ev->set_relative(ev->get_position() - prev); + prev = ev->get_position(); + + _input->parse_input_event(ev); + } + + if (touch_event->touches[lowest_id_index].isChanged) { + + Ref<InputEventMouseMotion> ev_mouse; + ev_mouse.instance(); + dom2godot_mod(touch_event, ev_mouse); + ev_mouse->set_button_mask(_input->get_mouse_button_mask() >> 1); + + const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index]; + ev_mouse->set_position(Point2(first_touch.canvasX, first_touch.canvasY)); + ev_mouse->set_global_position(ev_mouse->get_position()); + + ev_mouse->set_relative(_input->get_mouse_position() - ev_mouse->get_position()); + _input->set_mouse_position(ev_mouse->get_position()); + ev_mouse->set_speed(_input->get_last_mouse_speed()); + + _input->parse_input_event(ev_mouse); + } + return true; +} + +static Ref<InputEventKey> _setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) { - InputEvent ev; - ev.type = InputEvent::KEY; - ev.key.echo = emscripten_event->repeat; - ev.key.mod.alt = emscripten_event->altKey; - ev.key.mod.shift = emscripten_event->shiftKey; - ev.key.mod.control = emscripten_event->ctrlKey; - ev.key.mod.meta = emscripten_event->metaKey; - ev.key.scancode = dom2godot_scancode(emscripten_event->keyCode); + Ref<InputEventKey> ev; + ev.instance(); + ev->set_echo(emscripten_event->repeat); + dom2godot_mod(emscripten_event, ev); + ev->set_scancode(dom2godot_scancode(emscripten_event->keyCode)); String unicode = String::utf8(emscripten_event->key); // check if empty or multi-character (e.g. `CapsLock`) @@ -147,26 +364,26 @@ static InputEvent _setup_key_event(const EmscriptenKeyboardEvent *emscripten_eve unicode = String::utf8(emscripten_event->charValue); } if (unicode.length() == 1) { - ev.key.unicode = unicode[0]; + ev->set_unicode(unicode[0]); } return ev; } -static InputEvent deferred_key_event; +static Ref<InputEventKey> deferred_key_event; static EM_BOOL _keydown_callback(int event_type, const EmscriptenKeyboardEvent *key_event, void *user_data) { ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYDOWN, false); - InputEvent ev = _setup_key_event(key_event); - ev.key.pressed = true; - if (ev.key.unicode == 0 && keycode_has_unicode(ev.key.scancode)) { + Ref<InputEventKey> ev = _setup_key_event(key_event); + ev->set_pressed(true); + if (ev->get_unicode() == 0 && keycode_has_unicode(ev->get_scancode())) { // defer to keypress event for legacy unicode retrieval deferred_key_event = ev; return false; // do not suppress keypress event } - static_cast<OS_JavaScript *>(user_data)->push_input(ev); + _input->parse_input_event(ev); return true; } @@ -174,8 +391,8 @@ static EM_BOOL _keypress_callback(int event_type, const EmscriptenKeyboardEvent ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYPRESS, false); - deferred_key_event.key.unicode = key_event->charCode; - static_cast<OS_JavaScript *>(user_data)->push_input(deferred_key_event); + deferred_key_event->set_unicode(key_event->charCode); + _input->parse_input_event(deferred_key_event); return true; } @@ -183,10 +400,10 @@ static EM_BOOL _keyup_callback(int event_type, const EmscriptenKeyboardEvent *ke ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYUP, false); - InputEvent ev = _setup_key_event(key_event); - ev.key.pressed = false; - static_cast<OS_JavaScript *>(user_data)->push_input(ev); - return ev.key.scancode != KEY_UNKNOWN && ev.key.scancode != 0; + Ref<InputEventKey> ev = _setup_key_event(key_event); + ev->set_pressed(false); + _input->parse_input_event(ev); + return ev->get_scancode() != KEY_UNKNOWN && ev->get_scancode() != 0; } static EM_BOOL joy_callback_func(int p_type, const EmscriptenGamepadEvent *p_event, void *p_user) { @@ -197,18 +414,31 @@ static EM_BOOL joy_callback_func(int p_type, const EmscriptenGamepadEvent *p_eve return false; } +extern "C" { +void send_notification(int notif) { + if (notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || notif == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) { + _cursor_inside_canvas = notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER; + } + OS_JavaScript::get_singleton()->get_main_loop()->notification(notif); +} +} + void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { print_line("Init OS"); - if (gfx_init_func) - gfx_init_func(gfx_init_ud, use_gl2, p_desired.width, p_desired.height, p_desired.fullscreen); + EmscriptenWebGLContextAttributes attributes; + emscripten_webgl_init_context_attributes(&attributes); + attributes.alpha = false; + attributes.antialias = false; + attributes.majorVersion = 2; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(NULL, &attributes); + ERR_FAIL_COND(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS); - // nothing to do here, can't fulfil fullscreen request due to - // browser security, window size is already set from HTML video_mode = p_desired; + // can't fulfil fullscreen request due to browser security video_mode.fullscreen = false; - _windowed_size = get_window_size(); + set_window_size(Size2(p_desired.width, p_desired.height)); // find locale, emscripten only sets "C" char locale_ptr[16]; @@ -246,7 +476,7 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i print_line("Init VS"); visual_server = memnew(VisualServerRaster()); - visual_server->cursor_set_visible(false, 0); + // visual_server->cursor_set_visible(false, 0); print_line("Init Physicsserver"); @@ -256,25 +486,34 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i physics_2d_server->init(); input = memnew(InputDefault); + _input = input; power_manager = memnew(PowerJavascript); #define EM_CHECK(ev) \ if (result != EMSCRIPTEN_RESULT_SUCCESS) \ ERR_PRINTS("Error while setting " #ev " callback: Code " + itos(result)) -#define SET_EM_CALLBACK(ev, cb) \ - result = emscripten_set_##ev##_callback(NULL, this, true, &cb); \ +#define SET_EM_CALLBACK(target, ev, cb) \ + result = emscripten_set_##ev##_callback(target, this, true, &cb); \ EM_CHECK(ev) #define SET_EM_CALLBACK_NODATA(ev, cb) \ result = emscripten_set_##ev##_callback(NULL, true, &cb); \ EM_CHECK(ev) EMSCRIPTEN_RESULT result; - SET_EM_CALLBACK(keydown, _keydown_callback) - SET_EM_CALLBACK(keypress, _keypress_callback) - SET_EM_CALLBACK(keyup, _keyup_callback) - SET_EM_CALLBACK(resize, _browser_resize_callback) - SET_EM_CALLBACK(fullscreenchange, _fullscreen_change_callback) + SET_EM_CALLBACK("#window", mousemove, _mousemove_callback) + SET_EM_CALLBACK("#canvas", mousedown, _mousebutton_callback) + SET_EM_CALLBACK("#window", mouseup, _mousebutton_callback) + SET_EM_CALLBACK("#window", wheel, _wheel_callback) + SET_EM_CALLBACK("#window", touchstart, _touchpress_callback) + SET_EM_CALLBACK("#window", touchmove, _touchmove_callback) + SET_EM_CALLBACK("#window", touchend, _touchpress_callback) + SET_EM_CALLBACK("#window", touchcancel, _touchpress_callback) + SET_EM_CALLBACK("#canvas", keydown, _keydown_callback) + SET_EM_CALLBACK("#canvas", keypress, _keypress_callback) + SET_EM_CALLBACK("#canvas", keyup, _keyup_callback) + SET_EM_CALLBACK(NULL, resize, _browser_resize_callback) + SET_EM_CALLBACK(NULL, fullscreenchange, _fullscreen_change_callback) SET_EM_CALLBACK_NODATA(gamepadconnected, joy_callback_func) SET_EM_CALLBACK_NODATA(gamepaddisconnected, joy_callback_func) @@ -282,9 +521,24 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i #undef SET_EM_CALLBACK #undef EM_CHECK + /* clang-format off */ + EM_ASM_ARGS({ + const send_notification = Module.cwrap('send_notification', null, ['number']); + const notifs = arguments; + (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, i) { + Module.canvas.addEventListener(event, send_notification.bind(this, notifs[i])); + }); + }, + MainLoop::NOTIFICATION_WM_MOUSE_ENTER, + MainLoop::NOTIFICATION_WM_MOUSE_EXIT, + MainLoop::NOTIFICATION_WM_FOCUS_IN, + MainLoop::NOTIFICATION_WM_FOCUS_OUT + ); +/* clang-format on */ + #ifdef JAVASCRIPT_EVAL_ENABLED javascript_eval = memnew(JavaScript); - GlobalConfig::get_singleton()->add_singleton(GlobalConfig::Singleton("JavaScript", javascript_eval)); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("JavaScript", javascript_eval)); #endif visual_server->init(); @@ -315,30 +569,97 @@ void OS_JavaScript::alert(const String &p_alert, const String &p_title) { /* clang-format on */ } -void OS_JavaScript::set_mouse_show(bool p_show) { +static const char *godot2dom_cursor(OS::CursorShape p_shape) { + + switch (p_shape) { + case OS::CURSOR_ARROW: + default: + return "auto"; + case OS::CURSOR_IBEAM: return "text"; + case OS::CURSOR_POINTING_HAND: return "pointer"; + case OS::CURSOR_CROSS: return "crosshair"; + case OS::CURSOR_WAIT: return "progress"; + case OS::CURSOR_BUSY: return "wait"; + case OS::CURSOR_DRAG: return "grab"; + case OS::CURSOR_CAN_DROP: return "grabbing"; + case OS::CURSOR_FORBIDDEN: return "no-drop"; + case OS::CURSOR_VSIZE: return "ns-resize"; + case OS::CURSOR_HSIZE: return "ew-resize"; + case OS::CURSOR_BDIAGSIZE: return "nesw-resize"; + case OS::CURSOR_FDIAGSIZE: return "nwse-resize"; + case OS::CURSOR_MOVE: return "move"; + case OS::CURSOR_VSPLIT: return "row-resize"; + case OS::CURSOR_HSPLIT: return "col-resize"; + case OS::CURSOR_HELP: return "help"; + } +} + +void OS_JavaScript::set_css_cursor(const char *p_cursor) { - //javascript has no mouse... + /* clang-format off */ + EM_ASM_({ + Module.canvas.style.cursor = Module.UTF8ToString($0); + }, p_cursor); + /* clang-format on */ } -void OS_JavaScript::set_mouse_grab(bool p_grab) { +const char *OS_JavaScript::get_css_cursor() const { - //it really has no mouse...! + char cursor[16]; + /* clang-format off */ + EM_ASM_INT({ + Module.stringToUTF8(Module.canvas.style.cursor ? Module.canvas.style.cursor : 'auto', $0, 16); + }, cursor); + /* clang-format on */ + return cursor; } -bool OS_JavaScript::is_mouse_grab_enabled() const { +void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) { - //*sigh* technology has evolved so much since i was a kid.. - return false; + ERR_FAIL_INDEX(p_mode, MOUSE_MODE_CONFINED + 1); + ERR_EXPLAIN("MOUSE_MODE_CONFINED is not supported for the HTML5 platform"); + ERR_FAIL_COND(p_mode == MOUSE_MODE_CONFINED); + if (p_mode == get_mouse_mode()) + return; + + if (p_mode == MOUSE_MODE_VISIBLE) { + + set_css_cursor(godot2dom_cursor(cursor_shape)); + emscripten_exit_pointerlock(); + + } else if (p_mode == MOUSE_MODE_HIDDEN) { + + set_css_cursor("none"); + emscripten_exit_pointerlock(); + + } else if (p_mode == MOUSE_MODE_CAPTURED) { + + EMSCRIPTEN_RESULT result = emscripten_request_pointerlock("canvas", false); + ERR_EXPLAIN("MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback"); + ERR_FAIL_COND(result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED); + ERR_FAIL_COND(result != EMSCRIPTEN_RESULT_SUCCESS); + set_css_cursor(godot2dom_cursor(cursor_shape)); + } +} + +OS::MouseMode OS_JavaScript::get_mouse_mode() const { + + if (!strcmp(get_css_cursor(), "none")) + return MOUSE_MODE_HIDDEN; + + EmscriptenPointerlockChangeEvent ev; + emscripten_get_pointerlock_status(&ev); + return ev.isActive && (strcmp(ev.id, "canvas") == 0) ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE; } -Point2 OS_JavaScript::get_mouse_pos() const { +Point2 OS_JavaScript::get_mouse_position() const { - return input->get_mouse_pos(); + return input->get_mouse_position(); } int OS_JavaScript::get_mouse_button_state() const { - return last_button_mask; + return input->get_mouse_button_mask(); } void OS_JavaScript::set_window_title(const String &p_title) { @@ -376,14 +697,17 @@ Size2 OS_JavaScript::get_screen_size(int p_screen) const { void OS_JavaScript::set_window_size(const Size2 p_size) { - window_maximized = false; + windowed_size = p_size; if (is_window_fullscreen()) { + window_maximized = false; set_window_fullscreen(false); + } else if (is_window_maximized()) { + set_window_maximized(false); + } else { + video_mode.width = p_size.x; + video_mode.height = p_size.y; + emscripten_set_canvas_size(p_size.x, p_size.y); } - _windowed_size = p_size; - video_mode.width = p_size.x; - video_mode.height = p_size.y; - emscripten_set_canvas_size(p_size.x, p_size.y); } Size2 OS_JavaScript::get_window_size() const { @@ -396,20 +720,30 @@ Size2 OS_JavaScript::get_window_size() const { void OS_JavaScript::set_window_maximized(bool p_enabled) { window_maximized = p_enabled; - if (p_enabled) { - - if (is_window_fullscreen()) { - // _browser_resize callback will set canvas size - set_window_fullscreen(false); - } else { - /* clang-format off */ - video_mode.width = EM_ASM_INT_V(return window.innerWidth); - video_mode.height = EM_ASM_INT_V(return window.innerHeight); - /* clang-format on */ - emscripten_set_canvas_size(video_mode.width, video_mode.height); - } - } else { - set_window_size(_windowed_size); + if (is_window_fullscreen()) { + set_window_fullscreen(false); + return; + } + // Calling emscripten_enter_soft_fullscreen mutltiple times hides all + // page elements except the canvas permanently, so track state + if (p_enabled && !soft_fs_enabled) { + + EmscriptenFullscreenStrategy strategy; + strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; + strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; + strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; + strategy.canvasResizedCallback = NULL; + emscripten_enter_soft_fullscreen(NULL, &strategy); + soft_fs_enabled = true; + video_mode.width = get_window_size().width; + video_mode.height = get_window_size().height; + } else if (!p_enabled) { + + emscripten_exit_soft_fullscreen(); + soft_fs_enabled = false; + video_mode.width = windowed_size.width; + video_mode.height = windowed_size.height; + emscripten_set_canvas_size(video_mode.width, video_mode.height); } } @@ -423,9 +757,17 @@ void OS_JavaScript::set_window_fullscreen(bool p_enable) { // _browser_resize_callback or _fullscreen_change_callback EMSCRIPTEN_RESULT result; if (p_enable) { - /* clang-format off */ - EM_ASM(Module.requestFullscreen(false, false);); - /* clang-format on */ + if (window_maximized) { + // soft fs during real fs can cause issues + set_window_maximized(false); + window_maximized = true; + } + EmscriptenFullscreenStrategy strategy; + strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; + strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; + strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; + strategy.canvasResizedCallback = NULL; + emscripten_request_fullscreen_strategy(NULL, false, &strategy); } else { result = emscripten_exit_fullscreen(); if (result != EMSCRIPTEN_RESULT_SUCCESS) { @@ -439,6 +781,11 @@ bool OS_JavaScript::is_window_fullscreen() const { return video_mode.fullscreen; } +void OS_JavaScript::request_canvas_size_adjustment() { + + canvas_size_adjustment_requested = true; +} + void OS_JavaScript::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const { Size2 screen = get_screen_size(); @@ -462,7 +809,11 @@ bool OS_JavaScript::can_draw() const { void OS_JavaScript::set_cursor_shape(CursorShape p_shape) { - //javascript really really really has no mouse.. how amazing.. + ERR_FAIL_INDEX(p_shape, CURSOR_MAX); + + cursor_shape = p_shape; + if (get_mouse_mode() != MOUSE_MODE_HIDDEN) + set_css_cursor(godot2dom_cursor(cursor_shape)); } void OS_JavaScript::main_loop_begin() { @@ -494,6 +845,17 @@ bool OS_JavaScript::main_loop_iterate() { } } process_joypads(); + if (canvas_size_adjustment_requested) { + + if (video_mode.fullscreen || window_maximized) { + video_mode.width = get_window_size().width; + video_mode.height = get_window_size().height; + } + if (!video_mode.fullscreen) { + set_window_maximized(window_maximized); + } + canvas_size_adjustment_requested = false; + } return Main::iteration(); } @@ -503,222 +865,6 @@ void OS_JavaScript::main_loop_end() { main_loop->finish(); } -void OS_JavaScript::main_loop_focusout() { - - if (main_loop) - main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); - //audio_driver_javascript.set_pause(true); -} - -void OS_JavaScript::main_loop_focusin() { - - if (main_loop) - main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); - //audio_driver_javascript.set_pause(false); -} - -void OS_JavaScript::push_input(const InputEvent &p_ev) { - - InputEvent ev = p_ev; - ev.ID = last_id++; - if (ev.type == InputEvent::MOUSE_MOTION) { - input->set_mouse_pos(Point2(ev.mouse_motion.x, ev.mouse_motion.y)); - } else if (ev.type == InputEvent::MOUSE_BUTTON) { - last_button_mask = ev.mouse_button.button_mask; - } - input->parse_input_event(p_ev); -} - -void OS_JavaScript::process_touch(int p_what, int p_pointer, const Vector<TouchPos> &p_points) { - - //print_line("ev: "+itos(p_what)+" pnt: "+itos(p_pointer)+" pointc: "+itos(p_points.size())); - - switch (p_what) { - case 0: { //gesture begin - - if (touch.size()) { - //end all if exist - InputEvent ev; - ev.type = InputEvent::MOUSE_BUTTON; - ev.ID = last_id++; - ev.mouse_button.button_index = BUTTON_LEFT; - ev.mouse_button.button_mask = BUTTON_MASK_LEFT; - ev.mouse_button.pressed = false; - ev.mouse_button.x = touch[0].pos.x; - ev.mouse_button.y = touch[0].pos.y; - ev.mouse_button.global_x = touch[0].pos.x; - ev.mouse_button.global_y = touch[0].pos.y; - input->parse_input_event(ev); - - for (int i = 0; i < touch.size(); i++) { - - InputEvent ev; - ev.type = InputEvent::SCREEN_TOUCH; - ev.ID = last_id++; - ev.screen_touch.index = touch[i].id; - ev.screen_touch.pressed = false; - ev.screen_touch.x = touch[i].pos.x; - ev.screen_touch.y = touch[i].pos.y; - input->parse_input_event(ev); - } - } - - touch.resize(p_points.size()); - for (int i = 0; i < p_points.size(); i++) { - touch[i].id = p_points[i].id; - touch[i].pos = p_points[i].pos; - } - - { - //send mouse - InputEvent ev; - ev.type = InputEvent::MOUSE_BUTTON; - ev.ID = last_id++; - ev.mouse_button.button_index = BUTTON_LEFT; - ev.mouse_button.button_mask = BUTTON_MASK_LEFT; - ev.mouse_button.pressed = true; - ev.mouse_button.x = touch[0].pos.x; - ev.mouse_button.y = touch[0].pos.y; - ev.mouse_button.global_x = touch[0].pos.x; - ev.mouse_button.global_y = touch[0].pos.y; - last_mouse = touch[0].pos; - input->parse_input_event(ev); - } - - //send touch - for (int i = 0; i < touch.size(); i++) { - - InputEvent ev; - ev.type = InputEvent::SCREEN_TOUCH; - ev.ID = last_id++; - ev.screen_touch.index = touch[i].id; - ev.screen_touch.pressed = true; - ev.screen_touch.x = touch[i].pos.x; - ev.screen_touch.y = touch[i].pos.y; - input->parse_input_event(ev); - } - - } break; - case 1: { //motion - - if (p_points.size()) { - //send mouse, should look for point 0? - InputEvent ev; - ev.type = InputEvent::MOUSE_MOTION; - ev.ID = last_id++; - ev.mouse_motion.button_mask = BUTTON_MASK_LEFT; - ev.mouse_motion.x = p_points[0].pos.x; - ev.mouse_motion.y = p_points[0].pos.y; - input->set_mouse_pos(Point2(ev.mouse_motion.x, ev.mouse_motion.y)); - ev.mouse_motion.speed_x = input->get_last_mouse_speed().x; - ev.mouse_motion.speed_y = input->get_last_mouse_speed().y; - ev.mouse_motion.relative_x = p_points[0].pos.x - last_mouse.x; - ev.mouse_motion.relative_y = p_points[0].pos.y - last_mouse.y; - last_mouse = p_points[0].pos; - input->parse_input_event(ev); - } - - ERR_FAIL_COND(touch.size() != p_points.size()); - - for (int i = 0; i < touch.size(); i++) { - - int idx = -1; - for (int j = 0; j < p_points.size(); j++) { - - if (touch[i].id == p_points[j].id) { - idx = j; - break; - } - } - - ERR_CONTINUE(idx == -1); - - if (touch[i].pos == p_points[idx].pos) - continue; //no move unncesearily - - InputEvent ev; - ev.type = InputEvent::SCREEN_DRAG; - ev.ID = last_id++; - ev.screen_drag.index = touch[i].id; - ev.screen_drag.x = p_points[idx].pos.x; - ev.screen_drag.y = p_points[idx].pos.y; - ev.screen_drag.relative_x = p_points[idx].pos.x - touch[i].pos.x; - ev.screen_drag.relative_y = p_points[idx].pos.y - touch[i].pos.y; - input->parse_input_event(ev); - touch[i].pos = p_points[idx].pos; - } - - } break; - case 2: { //release - - if (touch.size()) { - //end all if exist - InputEvent ev; - ev.type = InputEvent::MOUSE_BUTTON; - ev.ID = last_id++; - ev.mouse_button.button_index = BUTTON_LEFT; - ev.mouse_button.button_mask = BUTTON_MASK_LEFT; - ev.mouse_button.pressed = false; - ev.mouse_button.x = touch[0].pos.x; - ev.mouse_button.y = touch[0].pos.y; - ev.mouse_button.global_x = touch[0].pos.x; - ev.mouse_button.global_y = touch[0].pos.y; - input->parse_input_event(ev); - - for (int i = 0; i < touch.size(); i++) { - - InputEvent ev; - ev.type = InputEvent::SCREEN_TOUCH; - ev.ID = last_id++; - ev.screen_touch.index = touch[i].id; - ev.screen_touch.pressed = false; - ev.screen_touch.x = touch[i].pos.x; - ev.screen_touch.y = touch[i].pos.y; - input->parse_input_event(ev); - } - touch.clear(); - } - - } break; - case 3: { // add tuchi - - ERR_FAIL_INDEX(p_pointer, p_points.size()); - - TouchPos tp = p_points[p_pointer]; - touch.push_back(tp); - - InputEvent ev; - ev.type = InputEvent::SCREEN_TOUCH; - ev.ID = last_id++; - ev.screen_touch.index = tp.id; - ev.screen_touch.pressed = true; - ev.screen_touch.x = tp.pos.x; - ev.screen_touch.y = tp.pos.y; - input->parse_input_event(ev); - - } break; - case 4: { - - for (int i = 0; i < touch.size(); i++) { - if (touch[i].id == p_pointer) { - - InputEvent ev; - ev.type = InputEvent::SCREEN_TOUCH; - ev.ID = last_id++; - ev.screen_touch.index = touch[i].id; - ev.screen_touch.pressed = false; - ev.screen_touch.x = touch[i].pos.x; - ev.screen_touch.y = touch[i].pos.y; - input->parse_input_event(ev); - touch.remove(i); - i--; - } - } - - } break; - } -} - void OS_JavaScript::process_accelerometer(const Vector3 &p_accelerometer) { input->set_accelerometer(p_accelerometer); @@ -726,7 +872,11 @@ void OS_JavaScript::process_accelerometer(const Vector3 &p_accelerometer) { bool OS_JavaScript::has_touchscreen_ui_hint() const { - return false; //??? + /* clang-format off */ + return EM_ASM_INT_V( + return 'ontouchstart' in window; + ); + /* clang-format on */ } void OS_JavaScript::main_loop_request_quit() { @@ -756,12 +906,12 @@ String OS_JavaScript::get_data_dir() const { return get_data_dir_func(); */ return "/userfs"; - //return GlobalConfig::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); + //return ProjectSettings::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); }; String OS_JavaScript::get_executable_path() const { - return String(); + return OS::get_executable_path(); } void OS_JavaScript::_close_notification_funcs(const String &p_file, int p_flags) { @@ -790,9 +940,9 @@ void OS_JavaScript::process_joypads() { InputDefault::JoyAxis jx; jx.min = 0; jx.value = value; - last_id = input->joy_axis(last_id, i, j, jx); + input->joy_axis(i, j, jx); } else { - last_id = input->joy_button(last_id, i, j, value); + input->joy_button(i, j, value); } } for (int j = 0; j < num_axes; j++) { @@ -800,7 +950,7 @@ void OS_JavaScript::process_joypads() { InputDefault::JoyAxis jx; jx.min = -1; jx.value = state.axis[j]; - last_id = input->joy_axis(last_id, i, j, jx); + input->joy_axis(i, j, jx); } } } @@ -839,14 +989,18 @@ int OS_JavaScript::get_power_percent_left() { return power_manager->get_power_percent_left(); } -OS_JavaScript::OS_JavaScript(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, GetDataDirFunc p_get_data_dir_func) { - gfx_init_func = p_gfx_init_func; - gfx_init_ud = p_gfx_init_ud; - last_button_mask = 0; +bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) { + + return p_feature == "web" || p_feature == "s3tc"; // TODO check for these features really being available +} + +OS_JavaScript::OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_dir_func) { + set_cmdline(p_execpath, get_cmdline_args()); main_loop = NULL; - last_id = 1; gl_extensions = NULL; window_maximized = false; + soft_fs_enabled = false; + canvas_size_adjustment_requested = false; get_data_dir_func = p_get_data_dir_func; FileAccessUnix::close_notification_func = _close_notification_funcs; diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index b16918b2da..13c500b3dc 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ @@ -44,25 +45,9 @@ #include <emscripten/html5.h> -typedef void (*GFXInitFunc)(void *ud, bool gl2, int w, int h, bool fs); typedef String (*GetDataDirFunc)(); class OS_JavaScript : public OS_Unix { -public: - struct TouchPos { - int id; - Point2 pos; - }; - -private: - Vector<TouchPos> touch; - Point2 last_mouse; - int last_button_mask; - unsigned int last_id; - GFXInitFunc gfx_init_func; - void *gfx_init_ud; - - bool use_gl2; int64_t time_to_save_sync; int64_t last_sync_time; @@ -74,8 +59,12 @@ private: const char *gl_extensions; InputDefault *input; + Vector2 windowed_size; bool window_maximized; + bool soft_fs_enabled; + bool canvas_size_adjustment_requested; VideoMode video_mode; + CursorShape cursor_shape; MainLoop *main_loop; GetDataDirFunc get_data_dir_func; @@ -90,6 +79,9 @@ private: void process_joypads(); + void set_css_cursor(const char *); + const char *get_css_cursor() const; + public: // functions used by main to initialize/deintialize the OS virtual int get_video_driver_count() const; @@ -119,10 +111,9 @@ public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); - virtual void set_mouse_show(bool p_show); - virtual void set_mouse_grab(bool p_grab); - virtual bool is_mouse_grab_enabled() const; - virtual Point2 get_mouse_pos() const; + virtual void set_mouse_mode(MouseMode p_mode); + virtual MouseMode get_mouse_mode() const; + virtual Point2 get_mouse_position() const; virtual int get_mouse_button_state() const; virtual void set_window_title(const String &p_title); @@ -142,6 +133,8 @@ public: virtual void set_window_fullscreen(bool p_enable); virtual bool is_window_fullscreen() const; + void request_canvas_size_adjustment(); + virtual String get_name(); virtual MainLoop *get_main_loop() const; @@ -166,8 +159,7 @@ public: virtual String get_resource_dir() const; void process_accelerometer(const Vector3 &p_accelerometer); - void process_touch(int p_what, int p_pointer, const Vector<TouchPos> &p_points); - void push_input(const InputEvent &p_ev); + void push_input(const Ref<InputEvent> &p_ev); virtual bool is_joy_known(int p_device); virtual String get_joy_guid(int p_device) const; @@ -177,7 +169,9 @@ public: virtual int get_power_seconds_left(); virtual int get_power_percent_left(); - OS_JavaScript(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, GetDataDirFunc p_get_data_dir_func); + virtual bool _check_internal_feature_support(const String &p_feature); + + OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_dir_func); ~OS_JavaScript(); }; diff --git a/platform/javascript/platform_config.h b/platform/javascript/platform_config.h index cdef185ff0..48bcadcc29 100644 --- a/platform/javascript/platform_config.h +++ b/platform/javascript/platform_config.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ diff --git a/platform/javascript/power_javascript.cpp b/platform/javascript/power_javascript.cpp index 24158a34fe..bd4502fc2a 100644 --- a/platform/javascript/power_javascript.cpp +++ b/platform/javascript/power_javascript.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ diff --git a/platform/javascript/power_javascript.h b/platform/javascript/power_javascript.h index 78a896c430..c7b853ce11 100644 --- a/platform/javascript/power_javascript.h +++ b/platform/javascript/power_javascript.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 */ diff --git a/platform/javascript/run_icon.png b/platform/javascript/run_icon.png Binary files differnew file mode 100644 index 0000000000..dedee6f479 --- /dev/null +++ b/platform/javascript/run_icon.png |