diff options
Diffstat (limited to 'platform/javascript')
-rw-r--r-- | platform/javascript/SCsub | 37 | ||||
-rw-r--r-- | platform/javascript/audio_driver_javascript.cpp | 14 | ||||
-rw-r--r-- | platform/javascript/audio_driver_javascript.h | 2 | ||||
-rw-r--r-- | platform/javascript/audio_server_javascript.cpp | 44 | ||||
-rw-r--r-- | platform/javascript/audio_server_javascript.h | 34 | ||||
-rw-r--r-- | platform/javascript/detect.py | 160 | ||||
-rw-r--r-- | platform/javascript/dom_keys.h | 2 | ||||
-rw-r--r-- | platform/javascript/export/export.cpp | 40 | ||||
-rw-r--r-- | platform/javascript/export/export.h | 2 | ||||
-rw-r--r-- | platform/javascript/godot_shell.html | 357 | ||||
-rw-r--r-- | platform/javascript/javascript_eval.cpp | 169 | ||||
-rw-r--r-- | platform/javascript/javascript_eval.h | 55 | ||||
-rw-r--r-- | platform/javascript/javascript_main.cpp | 19 | ||||
-rw-r--r-- | platform/javascript/logo.png | bin | 4807 -> 2316 bytes | |||
-rw-r--r-- | platform/javascript/os_javascript.cpp | 311 | ||||
-rw-r--r-- | platform/javascript/os_javascript.h | 39 | ||||
-rw-r--r-- | platform/javascript/platform_config.h | 2 |
17 files changed, 1050 insertions, 237 deletions
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index cd96cf4f31..5d5cd1590a 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -1,26 +1,37 @@ +#!/usr/bin/env python + Import('env') javascript_files = [ - "os_javascript.cpp", - "audio_driver_javascript.cpp", - "javascript_main.cpp", - "audio_server_javascript.cpp" + "os_javascript.cpp", + "audio_driver_javascript.cpp", + "javascript_main.cpp", + "audio_server_javascript.cpp", + "javascript_eval.cpp" ] -#obj = env.SharedObject('godot_javascript.cpp') - env_javascript = env.Clone() if env['target'] == "profile": - env_javascript.Append(CPPFLAGS=['-DPROFILER_ENABLED']) + env_javascript.Append(CPPFLAGS=['-DPROFILER_ENABLED']) -javascript_objects=[] +javascript_objects = [] for x in javascript_files: - javascript_objects.append( env_javascript.Object( x ) ) + 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']\""]) +env.Append(LINKFLAGS=["--shell-file", '"platform/javascript/godot_shell.html"']) -prog = None +build = env.Program('#bin/godot', javascript_objects, PROGSUFFIX=env["PROGSUFFIX"] + ".html") -#env_javascript.SharedLibrary("#platform/javascript/libgodot_javascript.so",[javascript_objects]) +def make_html_shell(target, source, env): + html_path = target[0].rstr() + assert html_path[:4] == 'bin/' + assert html_path[-5:] == '.html' + basename = html_path[4:-5] + with open(html_path, 'r+') as html_file: + fixed_html = html_file.read().replace('.html.mem', '.mem').replace(basename, '$GODOT_BASE') + html_file.seek(0) + html_file.truncate() + html_file.write(fixed_html) -env.Program('#bin/godot',javascript_objects,PROGSUFFIX=env["PROGSUFFIX"]+".html") +env.AddPostAction(build, Action(make_html_shell, "Creating HTML shell file")) diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp index 1b949e64f9..af9f28169b 100644 --- a/platform/javascript/audio_driver_javascript.cpp +++ b/platform/javascript/audio_driver_javascript.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -68,14 +68,18 @@ AudioDriverSW::OutputFormat AudioDriverJavaScript::get_output_format() const{ } void AudioDriverJavaScript::lock(){ - //if (active && mutex) - // mutex->lock(); + /* + if (active && mutex) + mutex->lock(); + */ } void AudioDriverJavaScript::unlock() { - //if (active && mutex) - // mutex->unlock(); + /* + if (active && mutex) + mutex->unlock(); + */ } void AudioDriverJavaScript::finish(){ diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h index c674a8566e..528b45569d 100644 --- a/platform/javascript/audio_driver_javascript.h +++ b/platform/javascript/audio_driver_javascript.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* 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 fbd5d2e1c0..5489f179eb 100644 --- a/platform/javascript/audio_server_javascript.cpp +++ b/platform/javascript/audio_server_javascript.cpp @@ -1,3 +1,31 @@ +/*************************************************************************/ +/* audio_server_javascript.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ #include "audio_server_javascript.h" #include "emscripten.h" @@ -59,7 +87,7 @@ const void* AudioServerJavascript::sample_get_data_ptr(RID p_sample) const{ return NULL; } -void AudioServerJavascript::sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer){ +void AudioServerJavascript::sample_set_data(RID p_sample, const PoolVector<uint8_t>& p_buffer){ Sample *sample = sample_owner.get(p_sample); ERR_FAIL_COND(!sample); @@ -67,7 +95,7 @@ void AudioServerJavascript::sample_set_data(RID p_sample, const DVector<uint8_t> Vector<float> buffer; buffer.resize(sample->length*chans); - DVector<uint8_t>::Read r=p_buffer.read(); + PoolVector<uint8_t>::Read r=p_buffer.read(); if (sample->format==SAMPLE_FORMAT_PCM8) { const int8_t*ptr = (const int8_t*)r.ptr(); for(int i=0;i<sample->length*chans;i++) { @@ -88,10 +116,10 @@ void AudioServerJavascript::sample_set_data(RID p_sample, const DVector<uint8_t> } -const DVector<uint8_t> AudioServerJavascript::sample_get_data(RID p_sample) const{ +PoolVector<uint8_t> AudioServerJavascript::sample_get_data(RID p_sample) const{ - return DVector<uint8_t>(); + return PoolVector<uint8_t>(); } void AudioServerJavascript::sample_set_mix_rate(RID p_sample,int p_rate){ @@ -589,9 +617,11 @@ void AudioServerJavascript::mix_to_js(int p_frames) { void AudioServerJavascript::init(){ - //EM_ASM( -// console.log('server is '+audio_server); -// ); + /* + EM_ASM( + console.log('server is '+audio_server); + ); + */ //int latency = GLOBAL_DEF("javascript/audio_latency",16384); diff --git a/platform/javascript/audio_server_javascript.h b/platform/javascript/audio_server_javascript.h index 1dc90c48ee..8e61e94dfc 100644 --- a/platform/javascript/audio_server_javascript.h +++ b/platform/javascript/audio_server_javascript.h @@ -1,3 +1,31 @@ +/*************************************************************************/ +/* audio_server_javascript.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 AUDIO_SERVER_JAVASCRIPT_H #define AUDIO_SERVER_JAVASCRIPT_H @@ -6,7 +34,7 @@ class AudioServerJavascript : public AudioServer { - OBJ_TYPE(AudioServerJavascript,AudioServer); + GDCLASS(AudioServerJavascript,AudioServer); enum { INTERNAL_BUFFER_SIZE=4096, @@ -103,8 +131,8 @@ public: virtual const void* sample_get_data_ptr(RID p_sample) const; - virtual void sample_set_data(RID p_sample, const DVector<uint8_t>& p_buffer); - virtual const DVector<uint8_t> sample_get_data(RID p_sample) const; + virtual void sample_set_data(RID p_sample, const PoolVector<uint8_t>& p_buffer); + virtual PoolVector<uint8_t> sample_get_data(RID p_sample) const; virtual void sample_set_mix_rate(RID p_sample,int p_rate); virtual int sample_get_mix_rate(RID p_sample) const; diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index ae33a43f0d..2cb6874000 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -2,106 +2,114 @@ import os import sys import string + def is_active(): - return True + return True + def get_name(): - return "JavaScript" + return "JavaScript" + def can_build(): - import os - if (not os.environ.has_key("EMSCRIPTEN_ROOT")): - return False - return True + import os + if (not os.environ.has_key("EMSCRIPTEN_ROOT")): + return False + return True -def get_opts(): - return [ - ['compress','Compress JS Executable','no'] - ] +def get_opts(): -def get_flags(): + return [ + ['wasm', 'Compile to WebAssembly', 'no'], + ['javascript_eval', 'Enable JavaScript eval interface', 'yes'], + ] - return [ - ('lua', 'no'), - ('tools', 'no'), - ('nedmalloc', 'no'), - ('theora', 'no'), - ('tools', 'no'), - ('nedmalloc', 'no'), - ('musepack', 'no'), - ('squirrel', 'no'), - ('squish', 'no'), - ('speex', 'no'), - ('old_scenes', 'no'), - ('etc1', 'no'), -# ('default_gui_theme', 'no'), - #('builtin_zlib', 'no'), - ] +def get_flags(): + return [ + ('tools', 'no'), + ('module_etc1_enabled', 'no'), + ('module_mpc_enabled', 'no'), + ('module_theora_enabled', 'no'), + ] def configure(env): - env.Append(CPPPATH=['#platform/javascript']) + env['ENV'] = os.environ + env.use_windows_spawn_fix('javascript') - em_path=os.environ["EMSCRIPTEN_ROOT"] + env.Append(CPPPATH=['#platform/javascript']) - env['ENV']['PATH'] = em_path+":"+env['ENV']['PATH'] + em_path = os.environ["EMSCRIPTEN_ROOT"] - env['CC'] = em_path+'/emcc' - env['CXX'] = em_path+'/emcc' - #env['AR'] = em_path+"/emar" - env['AR'] = em_path+"/emcc" - env['ARFLAGS'] = "-o" + env['ENV']['PATH'] = em_path + ":" + env['ENV']['PATH'] + env['CC'] = em_path + '/emcc' + env['CXX'] = em_path + '/emcc' + #env['AR'] = em_path+"/emar" + env['AR'] = em_path + "/emcc" + env['ARFLAGS'] = "-o" # env['RANLIB'] = em_path+"/emranlib" - env['RANLIB'] = em_path + "/emcc" - env['OBJSUFFIX'] = '.bc' - env['LIBSUFFIX'] = '.bc' - env['CCCOM'] = "$CC -o $TARGET $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES" - env['CXXCOM'] = "$CC -o $TARGET $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES" + env['RANLIB'] = em_path + "/emcc" + env['OBJSUFFIX'] = '.bc' + env['LIBSUFFIX'] = '.bc' + env['CCCOM'] = "$CC -o $TARGET $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES" + env['CXXCOM'] = "$CC -o $TARGET $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES" # env.Append(LIBS=['c','m','stdc++','log','GLESv1_CM','GLESv2']) # env["LINKFLAGS"]= string.split(" -g --sysroot="+ld_sysroot+" -Wl,--no-undefined -Wl,-z,noexecstack ") - if (env["target"]=="release"): - env.Append(CCFLAGS=['-O2']) - elif (env["target"]=="release_debug"): - env.Append(CCFLAGS=['-O2','-DDEBUG_ENABLED']) - elif (env["target"]=="debug"): - env.Append(CCFLAGS=['-D_DEBUG', '-Wall', '-O2', '-DDEBUG_ENABLED']) - #env.Append(CCFLAGS=['-D_DEBUG', '-Wall', '-g4', '-DDEBUG_ENABLED']) - env.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC']) - - if(env["opus"]=="yes"): - env.opus_fixed_point="yes" - - env.Append(CPPFLAGS=["-fno-exceptions",'-DNO_SAFE_CAST','-fno-rtti']) - env.Append(CPPFLAGS=['-DJAVASCRIPT_ENABLED', '-DUNIX_ENABLED', '-DPTHREAD_NO_RENAME', '-DNO_FCNTL','-DMPC_FIXED_POINT','-DTYPED_METHOD_BIND','-DNO_THREADS']) - env.Append(CPPFLAGS=['-DGLES2_ENABLED']) - env.Append(CPPFLAGS=['-DGLES_NO_CLIENT_ARRAYS']) - env.Append(CPPFLAGS=['-s','ASM_JS=1']) - env.Append(CPPFLAGS=['-s','FULL_ES2=1']) + if (env["target"] == "release"): + env.Append(CCFLAGS=['-O2']) + elif (env["target"] == "release_debug"): + env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) + elif (env["target"] == "debug"): + env.Append(CCFLAGS=['-D_DEBUG', '-Wall', '-O2', '-DDEBUG_ENABLED']) + #env.Append(CCFLAGS=['-D_DEBUG', '-Wall', '-g4', '-DDEBUG_ENABLED']) + env.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC']) + + # 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" + + # 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', '-DNO_FCNTL', '-DMPC_FIXED_POINT', '-DTYPED_METHOD_BIND', '-DNO_THREADS']) + env.Append(CPPFLAGS=['-DGLES2_ENABLED']) + env.Append(CPPFLAGS=['-DGLES_NO_CLIENT_ARRAYS']) + env.Append(CPPFLAGS=['-s', 'FULL_ES2=1']) # env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED','-DMPC_FIXED_POINT']) - if (env["compress"]=="yes"): - lzma_binpath = em_path+"/third_party/lzma.js/lzma-native" - lzma_decoder = em_path+"/third_party/lzma.js/lzma-decoder.js" - lzma_dec = "LZMA.decompress" - env.Append(LINKFLAGS=['--compression',lzma_binpath+","+lzma_decoder+","+lzma_dec]) - - env.Append(LINKFLAGS=['-s','ASM_JS=1']) - env.Append(LINKFLAGS=['-O2']) - #env.Append(LINKFLAGS=['-g4']) - - #print "CCCOM is:", env.subst('$CCCOM') - #print "P: ", env['p'], " Platofrm: ", env['platform'] - - import methods - env.Append( BUILDERS = { 'GLSL120' : env.Builder(action = methods.build_legacygl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) - env.Append( BUILDERS = { 'GLSL' : env.Builder(action = methods.build_glsl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) - env.Append( BUILDERS = { 'GLSL120GLES' : env.Builder(action = methods.build_gles2_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) - #env.Append( BUILDERS = { 'HLSL9' : env.Builder(action = methods.build_hlsl_dx9_headers, suffix = 'hlsl.h',src_suffix = '.hlsl') } ) + if env['wasm'] == 'yes': + env.Append(LINKFLAGS=['-s', 'BINARYEN=1']) + # Maximum memory size is baked into the WebAssembly binary during + # compilation, so we need to enable memory growth to allow setting + # TOTAL_MEMORY at runtime. The value set at runtime must be higher than + # what is set during compilation, check TOTAL_MEMORY in Emscripten's + # src/settings.js for the default. + env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1']) + env["PROGSUFFIX"] += ".webassembly" + else: + env.Append(CPPFLAGS=['-s', 'ASM_JS=1']) + env.Append(LINKFLAGS=['-s', 'ASM_JS=1']) + env.Append(LINKFLAGS=['--separate-asm']) + + if env['javascript_eval'] == 'yes': + env.Append(CPPFLAGS=['-DJAVASCRIPT_EVAL_ENABLED']) + + env.Append(LINKFLAGS=['-O2']) + # env.Append(LINKFLAGS=['-g4']) + + # print "CCCOM is:", env.subst('$CCCOM') + # print "P: ", env['p'], " Platofrm: ", env['platform'] + + import methods + + env.Append(BUILDERS={'GLSL120': env.Builder(action=methods.build_legacygl_headers, suffix='glsl.h', src_suffix='.glsl')}) + env.Append(BUILDERS={'GLSL': env.Builder(action=methods.build_glsl_headers, suffix='glsl.h', src_suffix='.glsl')}) + env.Append(BUILDERS={'GLSL120GLES': env.Builder(action=methods.build_gles2_headers, suffix='glsl.h', src_suffix='.glsl')}) + #env.Append( BUILDERS = { 'HLSL9' : env.Builder(action = methods.build_hlsl_dx9_headers, suffix = 'hlsl.h',src_suffix = '.hlsl') } ) diff --git a/platform/javascript/dom_keys.h b/platform/javascript/dom_keys.h index 282b632e93..5ef212ce4a 100644 --- a/platform/javascript/dom_keys.h +++ b/platform/javascript/dom_keys.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* 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/export/export.cpp b/platform/javascript/export/export.cpp index de57d770c4..33776bc273 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,7 +40,7 @@ #include "string.h" class EditorExportPlatformJavaScript : public EditorExportPlatform { - OBJ_TYPE( EditorExportPlatformJavaScript,EditorExportPlatform ); + GDCLASS( EditorExportPlatformJavaScript,EditorExportPlatform ); String custom_release_package; String custom_debug_package; @@ -180,12 +180,10 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t>& p_html, const St 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_FS",p_name+"fs.js"); - current_line = current_line.replace("$GODOT_MEM",p_name+".mem"); - current_line = current_line.replace("$GODOT_JS",p_name+".js"); - current_line = current_line.replace("$GODOT_CANVAS_WIDTH",Globals::get_singleton()->get("display/width")); - current_line = current_line.replace("$GODOT_CANVAS_HEIGHT",Globals::get_singleton()->get("display/height")); - current_line = current_line.replace("$GODOT_HEAD_TITLE",!html_title.empty()?html_title:(String) Globals::get_singleton()->get("application/name")); + current_line = current_line.replace("$GODOT_BASE",p_name); + current_line = current_line.replace("$GODOT_CANVAS_WIDTH",GlobalConfig::get_singleton()->get("display/width")); + current_line = current_line.replace("$GODOT_CANVAS_HEIGHT",GlobalConfig::get_singleton()->get("display/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); @@ -195,8 +193,8 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t>& p_html, const St } CharString cs = strnew.utf8(); - p_html.resize(cs.size()); - for(int i=9;i<cs.size();i++) { + p_html.resize(cs.length()); + for(int i=9;i<cs.length();i++) { p_html[i]=cs[i]; } } @@ -267,7 +265,7 @@ Error EditorExportPlatformJavaScript::export_project(const String& p_path, bool FileAccess *f=FileAccess::open(p_path.get_base_dir()+"/data.pck",FileAccess::WRITE); if (!f) { - EditorNode::add_io_error("Could not create file for writing:\n"+p_path.basename()+"_files.js"); + EditorNode::add_io_error("Could not create file for writing:\n"+p_path.get_basename()+"_files.js"); return ERR_FILE_CANT_WRITE; } Error err = save_pack(f); @@ -309,24 +307,32 @@ Error EditorExportPlatformJavaScript::export_project(const String& p_path, bool if (file=="godot.html") { - _fix_html(data,p_path.get_file().basename(), p_debug); + _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().basename()+"fs.js"; + file=p_path.get_file().get_basename()+"fs.js"; } if (file=="godot.js") { - //_fix_godot(data); - file=p_path.get_file().basename()+".js"; + file=p_path.get_file().get_basename()+".js"; + } + + if (file=="godot.asm.js") { + + file=p_path.get_file().get_basename()+".asm.js"; } if (file=="godot.mem") { - //_fix_godot(data); - file=p_path.get_file().basename()+".mem"; + file=p_path.get_file().get_basename()+".mem"; + } + + if (file=="godot.wasm") { + + file=p_path.get_file().get_basename()+".wasm"; } String dst = p_path.get_base_dir().plus_file(file); diff --git a/platform/javascript/export/export.h b/platform/javascript/export/export.h index 2105141e58..59c0a67e6d 100644 --- a/platform/javascript/export/export.h +++ b/platform/javascript/export/export.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* 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 new file mode 100644 index 0000000000..3170d2bb9e --- /dev/null +++ b/platform/javascript/godot_shell.html @@ -0,0 +1,357 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang=""> +<head> + <meta charset="utf-8" /> + <title>$GODOT_HEAD_TITLE</title> + $GODOT_HEAD_INCLUDE + <style type="text/css"> + body { + margin: 0; + border: 0 none; + padding: 0; + text-align: center; + background-color: black; + font-family: $GODOT_STYLE_FONT_FAMILY; + } + + + /* Godot Engine default theme style + * ================================ */ + + .godot { + color: #e0e0e0; + background-color: #3b3943; + background-image: linear-gradient(to bottom, #403e48, #35333c); + border: 1px solid #45434e; + box-shadow: 0 0 1px 1px #2f2d35; + } + + button.godot { + font-family: $GODOT_STYLE_FONT_FAMILY; /* override user agent style */ + padding: 1px 5px; + background-color: #37353f; + background-image: linear-gradient(to bottom, #413e49, #3a3842); + border: 1px solid #514f5d; + border-radius: 1px; + box-shadow: 0 0 1px 1px #2a2930; + } + + button.godot:hover { + color: #f0f0f0; + background-color: #44414e; + background-image: linear-gradient(to bottom, #494652, #423f4c); + border: 1px solid #5a5667; + box-shadow: 0 0 1px 1px #26252b; + } + + button.godot:active { + color: #fff; + background-color: #3e3b46; + background-image: linear-gradient(to bottom, #36343d, #413e49); + border: 1px solid #4f4c59; + box-shadow: 0 0 1px 1px #26252b; + } + + button.godot:disabled { + color: rgba(230, 230, 230, 0.2); + background-color: #3d3d3d; + background-image: linear-gradient(to bottom, #434343, #393939); + border: 1px solid #474747; + box-shadow: 0 0 1px 1px #2d2b33; + } + + + /* Canvas / wrapper + * ================ */ + + #container { + display: inline-block; /* scale with canvas */ + vertical-align: top; /* prevent extra height */ + position: relative; /* root for absolutely positioned overlay */ + margin: 0; + border: 0 none; + padding: 0; + background-color: #111; + } + + #canvas { + display: block; + margin: 0 auto; + /* canvas must have border and padding set to zero to + * calculate cursor coordinates correctly */ + border: 0 none; + padding: 0; + } + + + /* Status display + * ============== */ + + #status-container { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + display: flex; + justify-content: center; + align-items: center; + /* don't consume click events - make children visible explicitly */ + visibility: hidden; + } + + #status { + visibility: visible; + padding: 4px 6px; + } + + + /* 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; + } + + #container: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[type="checkbox"] { + /* override user agent style */ + margin-left: 0; + } + + label > input { + vertical-align: middle; + } + + #display-output { display: none; } + + + /* Debug output + * ============ */ + + #output { + display: none; + margin: 6px auto; + border: 2px groove grey; + padding: 4px; + outline: none; + text-align: left; + white-space: pre-wrap; + font-size: small; + color: #eee; + background-color: black; + font-family: "Lucida Console", Monaco, monospace; + } + + + /* Export style include + * ==================== */ + + $GODOT_STYLE_INCLUDE + </style> +</head> +<body> + <div id="container"> + <canvas id="canvas" width="$GODOT_CANVAS_WIDTH" height="$GODOT_CANVAS_HEIGHT" onclick="canvas.ownerDocument.defaultView.focus();" 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';">Loading page...</span> + </div> + <div id="controls" class="godot"> + <label id="display-output"><input id="output-toggle" type="checkbox" autocomplete="off" onchange="Presentation.setOutputVisible(this.checked);" />display output</label> + <!-- hidden until implemented + <label><input id="lock-cursor" type="checkbox" autocomplete="off" />lock cursor</label> + <label><input id="resize-canvas" type="checkbox" autocomplete="off" />resize canvas</label> + --> + <button id="fullscreen" class="godot" type="button" disabled="disabled" autocomplete="off" onclick="Presentation.goFullscreen();">fullscreen</button> + </div> + </div> + <!-- Firefox adds extra space to textarea, but shouldn't matter too much https://bugzilla.mozilla.org/show_bug.cgi?id=33654 --> + <textarea id="output" rows="10" cols="100" readonly="readonly" style="resize:none"></textarea> + + <script type="text/javascript">//<![CDATA[ + var Presentation = (function() { + var statusElement = document.getElementById("status"); + var outputElement = document.getElementById("output"); + var doneLoading = false; + + function onLoaded() { + doneLoading = true; + var fullscreenButtonElement = document.getElementById("fullscreen"); + fullscreenButtonElement.disabled = false; + } + + var presentation = { + statusElement: statusElement, + outputElement: outputElement, + setOutputVisible: function setOutputVisible(visible) { + outputElement.style.display = (visible?"block":"none"); + }, + setStatusVisible: function setStatusVisible(visible) { + statusElement.style.visibility = (visible?"visible":"hidden"); + }, + setStatus: function setStatus(text) { + if (!text || text.length === 0) { + Presentation.setStatusVisible(false); + onLoaded(); + } else { + Presentation.setStatusVisible(true); + statusElement.innerHTML = text; + } + }, + goFullscreen: function goFullscreen() { + if (doneLoading) Module.requestFullScreen(false, false); + } + }; + + if ($GODOT_CONTROLS_ENABLED) { // controls enabled + (function() { + var controlsElement = document.getElementById("controls"); + controlsElement.style.visibility="visible"; + })(); + } + + if ($GODOT_DEBUG_ENABLED) { // debugging enabled + (function() { + var outputToggleLabel = document.getElementById("display-output"); + var outputToggle = document.getElementById("output-toggle"); + + outputElement.value = ""; // clear browser cache + outputElement.style.display = "block"; + outputToggle.checked = true; + outputToggleLabel.style.display = "inline"; + + presentation.print = function print(text) { + if (outputElement.value.length !== 0) + outputElement.value += "\n"; + outputElement.value += text; + outputElement.scrollTop = outputElement.scrollHeight; // focus on bottom + }; + })(); + } + + return presentation; + })(); + + // Emscripten interface + var Module = (function() { + var print = (function() { + if (typeof Presentation.print === "function") { + return function print(text) { + if (arguments.length > 1) + text = Array.prototype.slice.call(arguments).join(" "); + console.log(text); + Presentation.print(text); + }; + } else { + return function print(text) { + if (arguments.length > 1) + text = Array.prototype.slice.call(arguments).join(" "); + console.log(text); + }; + } + })(); + + var canvas = (function() { + var canvasElement = 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 + canvasElement.addEventListener("webglcontextlost", function(e) { alert("WebGL context lost. Plase reload the page."); e.preventDefault(); }, false); + + return canvasElement; + })(); + + var setStatus = (function() { + if (typeof Presentation.setStatus === "function") + return function setStatus(text) { + if (!Module.setStatus.last) + Module.setStatus.last = { time: Date.now(), text: "" }; + if (text === Module.setStatus.text) + return; + 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]; + } + Presentation.setStatus(text); + }; + else + return function setStatus(text) { + if (!Module.setStatus.last) + Module.setStatus.last = { time: Date.now(), text: "" }; + if (text === Module.setStatus.text) + return; + 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]; + } + }; + })(); + + return { + TOTAL_MEMORY: 268435456, + preRun: [], + postRun: [], + print: print, + printErr: function printErr(text) { + if (arguments.length > 1) + text = Array.prototype.slice.call(arguments).join(" "); + if (0) { // XXX disabled for safety `if (typeof dump == "function")` + dump(text + "\n"); // fast, straight to the real console + } else { + console.error(text); + } + }, + canvas: canvas, + setStatus: setStatus, + totalDependencies: 0, + monitorRunDependencies: function monitorRunDependencies(left) { + this.totalDependencies = Math.max(this.totalDependencies, left); + Module.setStatus(left ? "Preparing... (" + (this.totalDependencies-left) + "/" + this.totalDependencies + ")" : "All downloads complete."); + } + }; + })(); + + Presentation.setStatus("Downloading..."); + + window.onerror = function(event) { + // TODO: do not warn on ok events like simulating an infinite loop or exitStatus + Module.setStatus("Exception thrown, see JavaScript console"); + Module.setStatus = function(text) { + if (text) Module.printErr("[post-exception status] " + text); + }; + }; + //]]></script> + <script type="text/javascript" src="$GODOT_BASEfs.js"></script> + {{{ SCRIPT }}} +</body> +</html> diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp new file mode 100644 index 0000000000..7bbe0ae99b --- /dev/null +++ b/platform/javascript/javascript_eval.cpp @@ -0,0 +1,169 @@ +/*************************************************************************/ +/* javascript_eval.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/*************************************************************************/ +#ifdef JAVASCRIPT_EVAL_ENABLED + +#include "javascript_eval.h" +#include "emscripten.h" + +JavaScript *JavaScript::singleton=NULL; + +JavaScript *JavaScript::get_singleton() { + + return singleton; +} + +Variant JavaScript::eval(const String& p_code, bool p_use_global_exec_context) { + + union { int i; double d; char* s; } js_data[4]; + Variant::Type return_type = static_cast<Variant::Type>(EM_ASM_INT({ + + var eval_ret; + try { + if ($3) { // p_use_global_exec_context + // indirect eval call grants global execution context + var global_eval = eval; + eval_ret = global_eval(UTF8ToString($2)); + } + else { + eval_ret = eval(UTF8ToString($2)); + } + } catch (e) { + Module.printErr(e); + eval_ret = null; + } + + switch (typeof eval_ret) { + + case 'boolean': + // bitwise op yields 32-bit int + setValue($0, eval_ret|0, 'i32'); + return 1; // BOOL + + case 'number': + if ((eval_ret|0)===eval_ret) { + setValue($0, eval_ret|0, 'i32'); + return 2; // INT + } + setValue($0, eval_ret, 'double'); + return 3; // REAL + + case 'string': + var array_len = lengthBytesUTF8(eval_ret)+1; + var array_ptr = _malloc(array_len); + try { + if (array_ptr===0) { + throw new Error('String allocation failed (probably out of memory)'); + } + setValue($0, array_ptr|0 , '*'); + stringToUTF8(eval_ret, array_ptr, array_len); + return 4; // STRING + } catch (e) { + if (array_ptr!==0) { + _free(array_ptr) + } + Module.printErr(e); + // fall through + } + break; + + case 'object': + if (eval_ret === null) { + break; + } + + else if (typeof eval_ret.x==='number' && typeof eval_ret.y==='number') { + setValue($0, eval_ret.x, 'double'); + setValue($0+$1, eval_ret.y, 'double'); + if (typeof eval_ret.z==='number') { + setValue($0+$1*2, eval_ret.z, 'double'); + return 7; // VECTOR3 + } + else if (typeof eval_ret.width==='number' && typeof eval_ret.height==='number') { + setValue($0+$1*2, eval_ret.width, 'double'); + setValue($0+$1*3, eval_ret.height, 'double'); + return 6; // RECT2 + } + return 5; // VECTOR2 + } + + else if (typeof eval_ret.r==='number' && typeof eval_ret.g==='number' && typeof eval_ret.b==='number') { + // assume 8-bit rgb components since we're on the web + setValue($0, eval_ret.r, 'double'); + setValue($0+$1, eval_ret.g, 'double'); + setValue($0+$1*2, eval_ret.b, 'double'); + setValue($0+$1*3, typeof eval_ret.a==='number' ? eval_ret.a : 1, 'double'); + return 14; // COLOR + } + break; + } + return 0; // NIL + + }, js_data, sizeof *js_data, p_code.utf8().get_data(), p_use_global_exec_context)); + + switch(return_type) { + case Variant::BOOL: + return !!js_data->i; + case Variant::INT: + return js_data->i; + case Variant::REAL: + return js_data->d; + case Variant::STRING: + { + String str = String::utf8(js_data->s); + EM_ASM_({ _free($0); }, js_data->s); + return str; + } + case Variant::VECTOR2: + return Vector2(js_data[0].d, js_data[1].d); + case Variant::VECTOR3: + return Vector3(js_data[0].d, js_data[1].d, js_data[2].d); + case Variant::RECT2: + return Rect2(js_data[0].d, js_data[1].d, js_data[2].d, js_data[3].d); + case Variant::COLOR: + return Color(js_data[0].d/255., js_data[1].d/255., js_data[2].d/255., js_data[3].d); + } + return Variant(); +} + +void JavaScript::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("eval", "code", "use_global_execution_context"), &JavaScript::eval, false); +} + +JavaScript::JavaScript() { + + ERR_FAIL_COND(singleton != NULL); + singleton = this; +} + +JavaScript::~JavaScript() { + +} + +#endif // JAVASCRIPT_EVAL_ENABLED diff --git a/platform/javascript/javascript_eval.h b/platform/javascript/javascript_eval.h new file mode 100644 index 0000000000..679224d519 --- /dev/null +++ b/platform/javascript/javascript_eval.h @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* javascript_eval.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/*************************************************************************/ +#ifdef JAVASCRIPT_EVAL_ENABLED + +#ifndef JAVASCRIPT_EVAL_H +#define JAVASCRIPT_EVAL_H + +#include "object.h" + +class JavaScript : public Object { +private: + GDCLASS( JavaScript, Object ); + + static JavaScript *singleton; + + +protected: + static void _bind_methods(); + +public: + Variant eval(const String& p_code, bool p_use_global_exec_context = false); + + static JavaScript *get_singleton(); + JavaScript(); + ~JavaScript(); +}; + +#endif // JAVASCRIPT_EVAL_H +#endif // JAVASCRIPT_EVAL_ENABLED diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp index d9342cfe10..6ec7f1ec3d 100644 --- a/platform/javascript/javascript_main.cpp +++ b/platform/javascript/javascript_main.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -66,11 +66,12 @@ static void _glut_mouse_button(int button, int state, int x, int y) { if (ev.mouse_button.button_index<4) { if (ev.mouse_button.pressed) { - _mouse_button_mask|=1<<ev.mouse_button.button_index; + _mouse_button_mask |= 1 << (ev.mouse_button.button_index-1); } else { - _mouse_button_mask&=~(1<<ev.mouse_button.button_index); + _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; @@ -79,6 +80,11 @@ static void _glut_mouse_button(int button, int state, int x, int y) { 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); + } } @@ -148,13 +154,13 @@ int main(int argc, char *argv[]) { /* Initialize the window */ printf("let it go!\n"); glutInit(&argc, argv); - os = new OS_JavaScript(_gfx_init,NULL,NULL,NULL,NULL); + 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[]={"-v",NULL};// -// Error err = Main::setup("",1,args); + //char *args[]={"-v",NULL};// + //Error err = Main::setup("",1,args); Error err = Main::setup("",0,NULL); #endif @@ -162,7 +168,6 @@ int main(int argc, char *argv[]) { glutMouseFunc(_glut_mouse_button); glutMotionFunc(_glut_mouse_motion); - glutMotionFunc(_glut_mouse_motion); glutPassiveMotionFunc(_glut_mouse_motion); diff --git a/platform/javascript/logo.png b/platform/javascript/logo.png Binary files differindex 07e0a41292..ce911180ac 100644 --- a/platform/javascript/logo.png +++ b/platform/javascript/logo.png diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 2e42e79996..18bf8f6a86 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,6 +37,7 @@ #include "main/main.h" #include "core/globals.h" +#include "stdlib.h" #include "emscripten.h" #include "dom_keys.h" @@ -77,6 +78,59 @@ void OS_JavaScript::set_opengl_extensions(const char* p_gl_extensions) { gl_extensions=p_gl_extensions; } +static EM_BOOL _browser_resize_callback(int event_type, const EmscriptenUiEvent *ui_event, void *user_data) { + + 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); + } + 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") { + + OS::VideoMode vm = os->get_video_mode(); + // this event property is the only reliable information on + // browser fullscreen state + vm.fullscreen = event->isFullscreen; + + 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); + } + } + } + return false; +} + static InputEvent _setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) { InputEvent ev; @@ -89,7 +143,9 @@ static InputEvent _setup_key_event(const EmscriptenKeyboardEvent *emscripten_eve ev.key.scancode = dom2godot_scancode(emscripten_event->keyCode); String unicode = String::utf8(emscripten_event->key); + // check if empty or multi-character (e.g. `CapsLock`) if (unicode.length()!=1) { + // might be empty as well, but better than nonsense unicode = String::utf8(emscripten_event->charValue); } if (unicode.length()==1) { @@ -151,7 +207,30 @@ void OS_JavaScript::initialize(const VideoMode& p_desired,int p_video_driver,int if (gfx_init_func) gfx_init_func(gfx_init_ud,use_gl2,p_desired.width,p_desired.height,p_desired.fullscreen); - default_videomode=p_desired; + // nothing to do here, can't fulfil fullscreen request due to + // browser security, window size is already set from HTML + video_mode=p_desired; + video_mode.fullscreen=false; + _windowed_size=get_window_size(); + + // find locale, emscripten only sets "C" + char locale_ptr[16]; + EM_ASM_({ + var locale = ""; + if (Module.locale) { + // best case: server-side script reads Accept-Language early and + // defines the locale to be read here + locale = Module.locale; + } else { + // no luck, use what the JS engine can tell us + // if this turns out not compatible enough, add tests for + // browserLanguage, systemLanguage and userLanguage + locale = navigator.languages ? navigator.languages[0] : navigator.language; + } + locale = locale.split('.')[0]; + stringToUTF8(locale, $0, 16); + }, locale_ptr); + setenv("LANG", locale_ptr, true); print_line("Init Audio"); @@ -164,7 +243,7 @@ void OS_JavaScript::initialize(const VideoMode& p_desired,int p_video_driver,int rasterizer_gles22->set_extensions(gl_extensions); rasterizer = rasterizer_gles22; } else { -// rasterizer = memnew( RasterizerGLES1(true, false) ); + //rasterizer = memnew( RasterizerGLES1(true, false) ); } print_line("Init VS"); @@ -210,26 +289,28 @@ void OS_JavaScript::initialize(const VideoMode& p_desired,int p_video_driver,int input = memnew( InputDefault ); - EMSCRIPTEN_RESULT result = emscripten_set_keydown_callback(NULL, this , true, &_keydown_callback); - if (result!=EMSCRIPTEN_RESULT_SUCCESS) { - ERR_PRINTS( "Error while setting Emscripten keydown callback: Code " + itos(result) ); - } - result = emscripten_set_keypress_callback(NULL, this, true, &_keypress_callback); - if (result!=EMSCRIPTEN_RESULT_SUCCESS) { - ERR_PRINTS( "Error while setting Emscripten keypress callback: Code " + itos(result) ); - } - result = emscripten_set_keyup_callback(NULL, this, true, &_keyup_callback); - if (result!=EMSCRIPTEN_RESULT_SUCCESS) { - ERR_PRINTS( "Error while setting Emscripten keyup callback: Code " + itos(result) ); - } - result = emscripten_set_gamepadconnected_callback(NULL, true, &joy_callback_func); - if (result!=EMSCRIPTEN_RESULT_SUCCESS) { - ERR_PRINTS( "Error while setting Emscripten gamepadconnected callback: Code " + itos(result) ); - } - result = emscripten_set_gamepaddisconnected_callback(NULL, true, &joy_callback_func); - if (result!=EMSCRIPTEN_RESULT_SUCCESS) { - ERR_PRINTS( "Error while setting Emscripten gamepaddisconnected callback: Code " + itos(result) ); - } +#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); 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_NODATA(gamepadconnected, joy_callback_func) + SET_EM_CALLBACK_NODATA(gamepaddisconnected, joy_callback_func) + +#undef SET_EM_CALLBACK_NODATA +#undef SET_EM_CALLBACK +#undef EM_CHECK + +#ifdef JAVASCRIPT_EVAL_ENABLED + javascript_eval = memnew(JavaScript); + GlobalConfig::get_singleton()->add_singleton(GlobalConfig::Singleton("JavaScript", javascript_eval)); +#endif } void OS_JavaScript::set_main_loop( MainLoop * p_main_loop ) { @@ -249,32 +330,11 @@ void OS_JavaScript::finalize() { memdelete(input); } +void OS_JavaScript::alert(const String& p_alert,const String& p_title) { -void OS_JavaScript::vprint(const char* p_format, va_list p_list, bool p_stderr) { - - if (p_stderr) { - - vfprintf(stderr,p_format,p_list); - fflush(stderr); - } else { - - vprintf(p_format,p_list); - fflush(stdout); - } -} - -void OS_JavaScript::print(const char *p_format, ... ) { - - va_list argp; - va_start(argp, p_format); - vprintf(p_format, argp ); - va_end(argp); - -} - -void OS_JavaScript::alert(const String& p_alert) { - - print("ALERT: %s\n",p_alert.utf8().get_data()); + EM_ASM_({ + window.alert(UTF8ToString($0)); + }, p_alert.utf8().get_data()); } @@ -293,17 +353,22 @@ bool OS_JavaScript::is_mouse_grab_enabled() const { //*sigh* technology has evolved so much since i was a kid.. return false; } + Point2 OS_JavaScript::get_mouse_pos() const { - return Point2(); + return input->get_mouse_pos(); } + int OS_JavaScript::get_mouse_button_state() const { - return 0; + return last_button_mask; } -void OS_JavaScript::set_window_title(const String& p_title) { +void OS_JavaScript::set_window_title(const String& p_title) { + EM_ASM_({ + document.title = UTF8ToString($0); + }, p_title.utf8().get_data()); } //interesting byt not yet @@ -312,22 +377,92 @@ void OS_JavaScript::set_window_title(const String& p_title) { void OS_JavaScript::set_video_mode(const VideoMode& p_video_mode,int p_screen) { - + video_mode = p_video_mode; } OS::VideoMode OS_JavaScript::get_video_mode(int p_screen) const { - return default_videomode; + return video_mode; +} + +Size2 OS_JavaScript::get_screen_size(int p_screen) const { + + ERR_FAIL_COND_V(p_screen!=0, Size2()); + + EmscriptenFullscreenChangeEvent ev; + EMSCRIPTEN_RESULT result = emscripten_get_fullscreen_status(&ev); + ERR_FAIL_COND_V(result!=EMSCRIPTEN_RESULT_SUCCESS, Size2()); + return Size2(ev.screenWidth, ev.screenHeight); +} + +void OS_JavaScript::set_window_size(const Size2 p_size) { + + window_maximized = false; + if (is_window_fullscreen()) { + set_window_fullscreen(false); + } + _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 { - return Vector2(default_videomode.width,default_videomode.height); + int canvas[3]; + emscripten_get_canvas_size(canvas, canvas+1, canvas+2); + return Size2(canvas[0], canvas[1]); +} + +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 { + video_mode.width = EM_ASM_INT_V(return window.innerWidth); + video_mode.height = EM_ASM_INT_V(return window.innerHeight); + emscripten_set_canvas_size(video_mode.width, video_mode.height); + } + } + else { + set_window_size(_windowed_size); + } +} + +void OS_JavaScript::set_window_fullscreen(bool p_enable) { + + if (p_enable==is_window_fullscreen()) { + return; + } + + // only requesting changes here, if successful, canvas is resized in + // _browser_resize_callback or _fullscreen_change_callback + EMSCRIPTEN_RESULT result; + if (p_enable) { + EM_ASM(Module.requestFullscreen(false, false);); + } + else { + result = emscripten_exit_fullscreen(); + if (result!=EMSCRIPTEN_RESULT_SUCCESS) { + ERR_PRINTS("Failed to exit fullscreen: Code " + itos(result)); + } + } +} + +bool OS_JavaScript::is_window_fullscreen() const { + + return video_mode.fullscreen; } void OS_JavaScript::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const { - p_list->push_back(default_videomode); + Size2 screen = get_screen_size(); + p_list->push_back(OS::VideoMode(screen.width, screen.height, true)); } String OS_JavaScript::get_name() { @@ -384,7 +519,7 @@ bool OS_JavaScript::main_loop_iterate() { } - process_joysticks(); + process_joypads(); return Main::iteration(); } @@ -415,12 +550,18 @@ 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())); + //print_line("ev: "+itos(p_what)+" pnt: "+itos(p_pointer)+" pointc: "+itos(p_points.size())); switch(p_what) { case 0: { //gesture begin @@ -503,8 +644,8 @@ void OS_JavaScript::process_touch(int p_what,int p_pointer, const Vector<TouchPo 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_mouse_speed().x; - ev.mouse_motion.speed_y=input->get_mouse_speed().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; @@ -641,48 +782,40 @@ void OS_JavaScript::main_loop_request_quit() { main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); } -void OS_JavaScript::set_display_size(Size2 p_size) { - - default_videomode.width=p_size.x; - default_videomode.height=p_size.y; -} - void OS_JavaScript::reload_gfx() { if (gfx_init_func) - gfx_init_func(gfx_init_ud,use_gl2,default_videomode.width,default_videomode.height,default_videomode.fullscreen); + gfx_init_func(gfx_init_ud,use_gl2,video_mode.width,video_mode.height,video_mode.fullscreen); if (rasterizer) rasterizer->reload_vram(); } Error OS_JavaScript::shell_open(String p_uri) { - - if (open_uri_func) - return open_uri_func(p_uri)?ERR_CANT_OPEN:OK; - return ERR_UNAVAILABLE; -}; + EM_ASM_({ + window.open(UTF8ToString($0), '_blank'); + }, p_uri.utf8().get_data()); + return OK; +} String OS_JavaScript::get_resource_dir() const { return "/"; //javascript has it's own filesystem for resources inside the APK } -String OS_JavaScript::get_locale() const { - - if (get_locale_func) - return get_locale_func(); - return OS_Unix::get_locale(); -} - - String OS_JavaScript::get_data_dir() const { - //if (get_data_dir_func) - // return get_data_dir_func(); + /* + if (get_data_dir_func) + return get_data_dir_func(); + */ return "/userfs"; - //return Globals::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); + //return GlobalConfig::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); }; +String OS_JavaScript::get_executable_path() const { + + return String(); +} void OS_JavaScript::_close_notification_funcs(const String& p_file,int p_flags) { @@ -693,7 +826,7 @@ void OS_JavaScript::_close_notification_funcs(const String& p_file,int p_flags) } } -void OS_JavaScript::process_joysticks() { +void OS_JavaScript::process_joypads() { int joy_count = emscripten_get_num_gamepads(); for (int i = 0; i < joy_count; i++) { @@ -749,24 +882,18 @@ String OS_JavaScript::get_joy_guid(int p_device) const { return input->get_joy_guid_remapped(p_device); } -OS_JavaScript::OS_JavaScript(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func,GetLocaleFunc p_get_locale_func) { - - - default_videomode.width=800; - default_videomode.height=600; - default_videomode.fullscreen=true; - default_videomode.resizable=false; +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; main_loop=NULL; last_id=1; gl_extensions=NULL; rasterizer=NULL; + window_maximized=false; - open_uri_func=p_open_uri_func; get_data_dir_func=p_get_data_dir_func; - get_locale_func=p_get_locale_func; FileAccessUnix::close_notification_func=_close_notification_funcs; time_to_save_sync=-1; diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index 16e4781d15..370322e93d 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,11 +42,10 @@ #include "audio_driver_javascript.h" #include "main/input_default.h" #include "emscripten/html5.h" +#include "javascript_eval.h" typedef void (*GFXInitFunc)(void *ud,bool gl2,int w, int h, bool fs); -typedef int (*OpenURIFunc)(const String&); typedef String (*GetDataDirFunc)(); -typedef String (*GetLocaleFunc)(); class OS_JavaScript : public OS_Unix { public: @@ -60,6 +59,7 @@ private: Vector<TouchPos> touch; Point2 last_mouse; + int last_button_mask; unsigned int last_id; GFXInitFunc gfx_init_func; void*gfx_init_ud; @@ -81,16 +81,19 @@ private: const char* gl_extensions; InputDefault *input; - VideoMode default_videomode; + bool window_maximized; + VideoMode video_mode; MainLoop * main_loop; - OpenURIFunc open_uri_func; GetDataDirFunc get_data_dir_func; - GetLocaleFunc get_locale_func; + +#ifdef JAVASCRIPT_EVAL_ENABLED + JavaScript* javascript_eval; +#endif static void _close_notification_funcs(const String& p_file,int p_flags); - void process_joysticks(); + void process_joypads(); public: @@ -116,9 +119,12 @@ public: //static OS* get_singleton(); - virtual void vprint(const char* p_format, va_list p_list, bool p_stderr=false); - virtual void print(const char *p_format, ... ); - virtual void alert(const String& p_alert); + virtual void print_error(const char* p_function, const char* p_file, int p_line, const char *p_code, const char* p_rationale, ErrorType p_type) { + + OS::print_error(p_function, p_file, p_line, p_code, p_rationale, p_type); + } + + virtual void alert(const String& p_alert,const String& p_title="ALERT!"); virtual void set_mouse_show(bool p_show); @@ -135,7 +141,15 @@ public: 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 Size2 get_screen_size(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 { return window_maximized; } + virtual void set_window_fullscreen(bool p_enable); + virtual bool is_window_fullscreen() const; + virtual String get_name(); virtual MainLoop *get_main_loop() const; @@ -153,14 +167,13 @@ public: virtual bool has_touchscreen_ui_hint() const; void set_opengl_extensions(const char* p_gl_extensions); - void set_display_size(Size2 p_size); void reload_gfx(); virtual Error shell_open(String p_uri); virtual String get_data_dir() const; + String get_executable_path() const; virtual String get_resource_dir() const; - virtual String get_locale() const; void process_accelerometer(const Vector3& p_accelerometer); void process_touch(int p_what,int p_pointer, const Vector<TouchPos>& p_points); @@ -170,7 +183,7 @@ public: virtual String get_joy_guid(int p_device) const; bool joy_connection_changed(int p_type, const EmscriptenGamepadEvent *p_event); - OS_JavaScript(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func,GetLocaleFunc p_get_locale_func); + OS_JavaScript(GFXInitFunc p_gfx_init_func,void*p_gfx_init_ud, GetDataDirFunc p_get_data_dir_func); ~OS_JavaScript(); }; diff --git a/platform/javascript/platform_config.h b/platform/javascript/platform_config.h index 143f16c1fa..cdef185ff0 100644 --- a/platform/javascript/platform_config.h +++ b/platform/javascript/platform_config.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ |