diff options
Diffstat (limited to 'platform/javascript')
-rw-r--r-- | platform/javascript/SCsub | 24 | ||||
-rw-r--r-- | platform/javascript/detect.py | 12 | ||||
-rw-r--r-- | platform/javascript/export/export.cpp | 11 | ||||
-rw-r--r-- | platform/javascript/godot_shell.html | 357 |
4 files changed, 388 insertions, 16 deletions
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index 22af436470..59e5aa175d 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -10,8 +10,6 @@ javascript_files = [ "javascript_eval.cpp" ] -#obj = env.SharedObject('godot_javascript.cpp') - env_javascript = env.Clone() if env['target'] == "profile": env_javascript.Append(CPPFLAGS=['-DPROFILER_ENABLED']) @@ -21,9 +19,19 @@ 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']\""]) - -prog = None - -#env_javascript.SharedLibrary("#platform/javascript/libgodot_javascript.so",[javascript_objects]) - -env.Program('#bin/godot',javascript_objects,PROGSUFFIX=env["PROGSUFFIX"]+".html") +env.Append(LINKFLAGS=["--shell-file",'"platform/javascript/godot_shell.html"']) + +build = env.Program('#bin/godot',javascript_objects,PROGSUFFIX=env["PROGSUFFIX"]+".html") + +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.AddPostAction(build, Action(make_html_shell, "Creating HTML shell file")) diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index b1277bfc05..b6a6a453b3 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -18,6 +18,7 @@ def can_build(): def get_opts(): return [ + ['wasm','Compile to WebAssembly','no'], ['javascript_eval','Enable JavaScript eval interface','yes'], ] @@ -42,7 +43,6 @@ def configure(env): em_path=os.environ["EMSCRIPTEN_ROOT"] env['ENV']['PATH'] = em_path+":"+env['ENV']['PATH'] - env['CC'] = em_path+'/emcc' env['CXX'] = em_path+'/emcc' #env['AR'] = em_path+"/emar" @@ -77,14 +77,20 @@ def configure(env): 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']) # env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED','-DMPC_FIXED_POINT']) + if env['wasm'] == 'yes': + env.Append(LINKFLAGS=['-s','BINARYEN=1']) + env.Append(LINKFLAGS=['-s','\'BINARYEN_METHOD="native-wasm"\'']) + env["PROGSUFFIX"]+=".webassembly" + else: + env.Append(CPPFLAGS=['-s','ASM_JS=1']) + env.Append(LINKFLAGS=['-s','ASM_JS=1']) + if env['javascript_eval'] == 'yes': env.Append(CPPFLAGS=['-DJAVASCRIPT_EVAL_ENABLED']) - env.Append(LINKFLAGS=['-s','ASM_JS=1']) env.Append(LINKFLAGS=['-O2']) #env.Append(LINKFLAGS=['-g4']) diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index f934916aa2..721aef3e50 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -180,9 +180,7 @@ 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_BASE",p_name); 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")); @@ -319,16 +317,19 @@ Error EditorExportPlatformJavaScript::export_project(const String& p_path, bool } if (file=="godot.js") { - //_fix_godot(data); file=p_path.get_file().basename()+".js"; } if (file=="godot.mem") { - //_fix_godot(data); file=p_path.get_file().basename()+".mem"; } + if (file=="godot.wasm") { + + file=p_path.get_file().basename()+".wasm"; + } + String dst = p_path.get_base_dir().plus_file(file); FileAccess *f=FileAccess::open(dst,FileAccess::WRITE); if (!f) { 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> |