diff options
Diffstat (limited to 'platform/web')
-rw-r--r-- | platform/web/detect.py | 14 | ||||
-rw-r--r-- | platform/web/display_server_web.cpp | 153 | ||||
-rw-r--r-- | platform/web/display_server_web.h | 28 | ||||
-rw-r--r-- | platform/web/export/export_plugin.cpp | 17 | ||||
-rw-r--r-- | platform/web/godot_js.h | 5 | ||||
-rw-r--r-- | platform/web/godot_webgl2.h | 2 | ||||
-rw-r--r-- | platform/web/js/libs/library_godot_input.js | 135 | ||||
-rw-r--r-- | platform/web/js/libs/library_godot_webgl2.externs.js | 16 | ||||
-rw-r--r-- | platform/web/js/libs/library_godot_webgl2.js | 17 |
9 files changed, 365 insertions, 22 deletions
diff --git a/platform/web/detect.py b/platform/web/detect.py index b0044a80ff..579eaaff03 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -186,9 +186,18 @@ def configure(env: "Environment"): env["LIBPREFIXES"] = ["$LIBPREFIX"] env["LIBSUFFIXES"] = ["$LIBSUFFIX"] + # Get version info for checks below. + cc_version = get_compiler_version(env) + cc_semver = (cc_version["major"], cc_version["minor"], cc_version["patch"]) + env.Prepend(CPPPATH=["#platform/web"]) env.Append(CPPDEFINES=["WEB_ENABLED", "UNIX_ENABLED"]) + if cc_semver >= (3, 1, 25): + env.Append(LINKFLAGS=["-s", "STACK_SIZE=5MB"]) + else: + env.Append(LINKFLAGS=["-s", "TOTAL_STACK=5MB"]) + if env["opengl3"]: env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"]) # This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1. @@ -203,13 +212,10 @@ def configure(env: "Environment"): env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"]) env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"]) env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"]) + env.Append(LINKFLAGS=["-s", "DEFAULT_PTHREAD_STACK_SIZE=2MB"]) env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"]) env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"]) - # Get version info for checks below. - cc_version = get_compiler_version(env) - cc_semver = (cc_version["major"], cc_version["minor"], cc_version["patch"]) - if env["lto"] != "none": # Workaround https://github.com/emscripten-core/emscripten/issues/19781. if cc_semver >= (3, 1, 42) and cc_semver < (3, 1, 46): diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index 022e044185..b4a190d47e 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -174,6 +174,11 @@ void DisplayServerWeb::_key_callback(const String &p_key_event_code, const Strin // Resume audio context after input in case autoplay was denied. OS_Web::get_singleton()->resume_audio(); + DisplayServerWeb *ds = get_singleton(); + if (ds->ime_started) { + return; + } + char32_t c = 0x00; String unicode = p_key_event_key; if (unicode.length() == 1) { @@ -183,17 +188,21 @@ void DisplayServerWeb::_key_callback(const String &p_key_event_code, const Strin Key keycode = dom_code2godot_scancode(p_key_event_code.utf8().get_data(), p_key_event_key.utf8().get_data(), false); Key scancode = dom_code2godot_scancode(p_key_event_code.utf8().get_data(), p_key_event_key.utf8().get_data(), true); - Ref<InputEventKey> ev; - ev.instantiate(); - ev->set_echo(p_repeat); - ev->set_keycode(fix_keycode(c, keycode)); - ev->set_physical_keycode(scancode); - ev->set_key_label(fix_key_label(c, keycode)); - ev->set_unicode(fix_unicode(c)); - ev->set_pressed(p_pressed); - dom2godot_mod(ev, p_modifiers, fix_keycode(c, keycode)); + DisplayServerWeb::KeyEvent ke; - Input::get_singleton()->parse_input_event(ev); + ke.pressed = p_pressed; + ke.echo = p_repeat; + ke.raw = true; + ke.keycode = fix_keycode(c, keycode); + ke.physical_keycode = scancode; + ke.key_label = fix_key_label(c, keycode); + ke.unicode = fix_unicode(c); + ke.mod = p_modifiers; + + if (ds->key_event_pos >= ds->key_event_buffer.size()) { + ds->key_event_buffer.resize(1 + ds->key_event_pos); + } + ds->key_event_buffer.write[ds->key_event_pos++] = ke; // Make sure to flush all events so we can call restricted APIs inside the event. Input::get_singleton()->flush_buffered_events(); @@ -619,7 +628,7 @@ int DisplayServerWeb::mouse_wheel_callback(double p_delta_x, double p_delta_y) { } int DisplayServerWeb::_mouse_wheel_callback(double p_delta_x, double p_delta_y) { - if (!godot_js_display_canvas_is_focused()) { + if (!godot_js_display_canvas_is_focused() && !godot_js_is_ime_focused()) { if (get_singleton()->cursor_inside_canvas) { godot_js_display_canvas_focus(); } else { @@ -726,7 +735,7 @@ bool DisplayServerWeb::is_touchscreen_available() const { // Virtual Keyboard void DisplayServerWeb::vk_input_text_callback(const char *p_text, int p_cursor) { - String text = p_text; + String text = String::utf8(p_text); #ifdef PROXY_TO_PTHREAD_ENABLED if (!Thread::is_main_thread()) { @@ -809,6 +818,100 @@ void DisplayServerWeb::_gamepad_callback(int p_index, int p_connected, const Str } } +// IME. +void DisplayServerWeb::ime_callback(int p_type, const char *p_text) { + String text = String::utf8(p_text); + +#ifdef PROXY_TO_PTHREAD_ENABLED + if (!Thread::is_main_thread()) { + callable_mp_static(DisplayServerWeb::_ime_callback).bind(p_type, text).call_deferred(); + return; + } +#endif + + _ime_callback(p_type, text); +} + +void DisplayServerWeb::_ime_callback(int p_type, const String &p_text) { + DisplayServerWeb *ds = get_singleton(); + // Resume audio context after input in case autoplay was denied. + OS_Web::get_singleton()->resume_audio(); + + switch (p_type) { + case 0: { + // IME start. + ds->ime_text = String(); + ds->ime_selection = Vector2i(); + for (int i = ds->key_event_pos - 1; i >= 0; i--) { + // Delete last raw keydown event from query. + if (ds->key_event_buffer[i].pressed && ds->key_event_buffer[i].raw) { + ds->key_event_buffer.remove_at(i); + ds->key_event_pos--; + break; + } + } + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE); + ds->ime_started = true; + } break; + case 1: { + // IME update. + if (ds->ime_active && ds->ime_started) { + ds->ime_text = p_text; + ds->ime_selection = Vector2i(ds->ime_text.length(), ds->ime_text.length()); + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE); + } + } break; + case 2: { + // IME commit. + if (ds->ime_active && ds->ime_started) { + ds->ime_started = false; + + ds->ime_text = String(); + ds->ime_selection = Vector2i(); + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE); + + String text = p_text; + for (int i = 0; i < text.length(); i++) { + DisplayServerWeb::KeyEvent ke; + + ke.pressed = true; + ke.echo = false; + ke.raw = false; + ke.keycode = Key::NONE; + ke.physical_keycode = Key::NONE; + ke.key_label = Key::NONE; + ke.unicode = text[i]; + ke.mod = 0; + + if (ds->key_event_pos >= ds->key_event_buffer.size()) { + ds->key_event_buffer.resize(1 + ds->key_event_pos); + } + ds->key_event_buffer.write[ds->key_event_pos++] = ke; + } + } + } break; + default: + break; + } +} + +void DisplayServerWeb::window_set_ime_active(const bool p_active, WindowID p_window) { + ime_active = p_active; + godot_js_set_ime_active(p_active); +} + +void DisplayServerWeb::window_set_ime_position(const Point2i &p_pos, WindowID p_window) { + godot_js_set_ime_position(p_pos.x, p_pos.y); +} + +Point2i DisplayServerWeb::ime_get_selection() const { + return ime_selection; +} + +String DisplayServerWeb::ime_get_text() const { + return ime_text; +} + void DisplayServerWeb::process_joypads() { Input *input = Input::get_singleton(); int32_t pads = godot_js_input_gamepad_sample_count(); @@ -893,6 +996,9 @@ void DisplayServerWeb::_send_window_event_callback(int p_notification) { if (p_notification == DisplayServer::WINDOW_EVENT_MOUSE_ENTER || p_notification == DisplayServer::WINDOW_EVENT_MOUSE_EXIT) { ds->cursor_inside_canvas = p_notification == DisplayServer::WINDOW_EVENT_MOUSE_ENTER; } + if (godot_js_is_ime_focused() && (p_notification == DisplayServer::WINDOW_EVENT_FOCUS_IN || p_notification == DisplayServer::WINDOW_EVENT_FOCUS_OUT)) { + return; + } if (!ds->window_event_callback.is_null()) { Variant event = int(p_notification); ds->window_event_callback.call(event); @@ -1003,6 +1109,7 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode godot_js_input_paste_cb(&DisplayServerWeb::update_clipboard_callback); godot_js_input_drop_files_cb(&DisplayServerWeb::drop_files_js_callback); godot_js_input_gamepad_cb(&DisplayServerWeb::gamepad_callback); + godot_js_set_ime_cb(&DisplayServerWeb::ime_callback, &DisplayServerWeb::key_callback, key_event.code, key_event.key); // JS Display interface (js/libs/library_godot_display.js) godot_js_display_fullscreen_cb(&DisplayServerWeb::fullscreen_change_callback); @@ -1030,7 +1137,6 @@ bool DisplayServerWeb::has_feature(Feature p_feature) const { switch (p_feature) { //case FEATURE_GLOBAL_MENU: //case FEATURE_HIDPI: - //case FEATURE_IME: case FEATURE_ICON: case FEATURE_CLIPBOARD: case FEATURE_CURSOR_SHAPE: @@ -1044,6 +1150,9 @@ bool DisplayServerWeb::has_feature(Feature p_feature) const { //case FEATURE_WINDOW_TRANSPARENCY: //case FEATURE_KEEP_SCREEN_ON: //case FEATURE_ORIENTATION: + case FEATURE_IME: + // IME does not work with experimental VK support. + return godot_js_display_vk_available() == 0; case FEATURE_VIRTUAL_KEYBOARD: return godot_js_display_vk_available() != 0; case FEATURE_TEXT_TO_SPEECH: @@ -1263,6 +1372,24 @@ void DisplayServerWeb::process_events() { Input::get_singleton()->flush_buffered_events(); if (godot_js_input_gamepad_sample() == OK) { process_joypads(); + for (int i = 0; i < key_event_pos; i++) { + const DisplayServerWeb::KeyEvent &ke = key_event_buffer[i]; + + Ref<InputEventKey> ev; + ev.instantiate(); + ev->set_pressed(ke.pressed); + ev->set_echo(ke.echo); + ev->set_keycode(ke.keycode); + ev->set_physical_keycode(ke.physical_keycode); + ev->set_key_label(ke.key_label); + ev->set_unicode(ke.unicode); + if (ke.raw) { + dom2godot_mod(ev, ke.mod, ke.keycode); + } + + Input::get_singleton()->parse_input_event(ev); + } + key_event_pos = 0; } } diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h index 51c6ab3c0a..140aef952b 100644 --- a/platform/web/display_server_web.h +++ b/platform/web/display_server_web.h @@ -82,6 +82,25 @@ private: uint64_t last_click_ms = 0; MouseButton last_click_button_index = MouseButton::NONE; + bool ime_active = false; + bool ime_started = false; + String ime_text; + Vector2i ime_selection; + + struct KeyEvent { + bool pressed = false; + bool echo = false; + bool raw = false; + Key keycode = Key::NONE; + Key physical_keycode = Key::NONE; + Key key_label = Key::NONE; + uint32_t unicode = 0; + int mod = 0; + }; + + Vector<KeyEvent> key_event_buffer; + int key_event_pos = 0; + bool swap_cancel_ok = false; bool tts = false; @@ -108,6 +127,8 @@ private: static void _gamepad_callback(int p_index, int p_connected, const String &p_id, const String &p_guid); WASM_EXPORT static void js_utterance_callback(int p_event, int p_id, int p_pos); static void _js_utterance_callback(int p_event, int p_id, int p_pos); + WASM_EXPORT static void ime_callback(int p_type, const char *p_text); + static void _ime_callback(int p_type, const String &p_text); WASM_EXPORT static void request_quit_callback(); static void _request_quit_callback(); WASM_EXPORT static void window_blur_callback(); @@ -162,6 +183,13 @@ public: virtual MouseMode mouse_get_mode() const override; virtual Point2i mouse_get_position() const override; + // ime + virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID) override; + virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID) override; + + virtual Point2i ime_get_selection() const override; + virtual String ime_get_text() const override; + // touch virtual bool is_touchscreen_available() const override; diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index a62ccdc2aa..a70812cf5b 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -259,6 +259,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese _replace_strings(replaces, sw); Error err = _write_or_error(sw.ptr(), sw.size(), dir.path_join(name + ".service.worker.js")); if (err != OK) { + // Message is supplied by the subroutine method. return err; } @@ -291,16 +292,19 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese const String icon144_path = p_preset->get("progressive_web_app/icon_144x144"); err = _add_manifest_icon(p_path, icon144_path, 144, icons_arr); if (err != OK) { + // Message is supplied by the subroutine method. return err; } const String icon180_path = p_preset->get("progressive_web_app/icon_180x180"); err = _add_manifest_icon(p_path, icon180_path, 180, icons_arr); if (err != OK) { + // Message is supplied by the subroutine method. return err; } const String icon512_path = p_preset->get("progressive_web_app/icon_512x512"); err = _add_manifest_icon(p_path, icon512_path, 512, icons_arr); if (err != OK) { + // Message is supplied by the subroutine method. return err; } manifest["icons"] = icons_arr; @@ -308,6 +312,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese CharString cs = Variant(manifest).to_json_string().utf8(); err = _write_or_error((const uint8_t *)cs.get_data(), cs.length(), dir.path_join(name + ".manifest.json")); if (err != OK) { + // Message is supplied by the subroutine method. return err; } @@ -439,6 +444,11 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p const String base_path = p_path.get_basename(); const String base_name = p_path.get_file().get_basename(); + if (!DirAccess::exists(base_dir)) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Target folder does not exist or is inaccessible: \"%s\""), base_dir)); + return ERR_FILE_BAD_PATH; + } + // Find the correct template String template_path = p_debug ? custom_debug : custom_release; template_path = template_path.strip_edges(); @@ -447,10 +457,6 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p template_path = find_export_template(_get_template_name(extensions, p_debug)); } - if (!DirAccess::exists(base_dir)) { - return ERR_FILE_BAD_PATH; - } - if (!template_path.is_empty() && !FileAccess::exists(template_path)) { add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Template file not found: \"%s\"."), template_path)); return ERR_FILE_NOT_FOUND; @@ -480,6 +486,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p // Extract templates. error = _extract_template(template_path, base_dir, base_name, pwa); if (error) { + // Message is supplied by the subroutine method. return error; } @@ -510,6 +517,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p _fix_html(html, p_preset, base_name, p_debug, p_flags, shared_objects, file_sizes); Error err = _write_or_error(html.ptr(), html.size(), p_path); if (err != OK) { + // Message is supplied by the subroutine method. return err; } html.resize(0); @@ -543,6 +551,7 @@ Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_p if (pwa) { err = _build_pwa(p_preset, p_path, shared_objects); if (err != OK) { + // Message is supplied by the subroutine method. return err; } } diff --git a/platform/web/godot_js.h b/platform/web/godot_js.h index 031e67e486..a3d2632f17 100644 --- a/platform/web/godot_js.h +++ b/platform/web/godot_js.h @@ -64,6 +64,11 @@ extern void godot_js_input_touch_cb(void (*p_callback)(int p_type, int p_count), extern void godot_js_input_key_cb(void (*p_callback)(int p_type, int p_repeat, int p_modifiers), char r_code[32], char r_key[32]); extern void godot_js_input_vibrate_handheld(int p_duration_ms); +extern void godot_js_set_ime_active(int p_active); +extern void godot_js_set_ime_position(int p_x, int p_y); +extern void godot_js_set_ime_cb(void (*p_input)(int p_type, const char *p_text), void (*p_callback)(int p_type, int p_repeat, int p_modifiers), char r_code[32], char r_key[32]); +extern int godot_js_is_ime_focused(); + // Input gamepad extern void godot_js_input_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid)); extern int godot_js_input_gamepad_sample(); diff --git a/platform/web/godot_webgl2.h b/platform/web/godot_webgl2.h index 3ade9e4239..2c9af4313f 100644 --- a/platform/web/godot_webgl2.h +++ b/platform/web/godot_webgl2.h @@ -44,9 +44,11 @@ extern "C" { #endif void godot_webgl2_glFramebufferTextureMultiviewOVR(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +void godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR(GLenum target, GLenum attachment, GLuint texture, GLint level, GLsizei samples, GLint baseViewIndex, GLsizei numViews); void godot_webgl2_glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data); #define glFramebufferTextureMultiviewOVR godot_webgl2_glFramebufferTextureMultiviewOVR +#define glFramebufferTextureMultisampleMultiviewOVR godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR #ifdef __cplusplus } diff --git a/platform/web/js/libs/library_godot_input.js b/platform/web/js/libs/library_godot_input.js index eaff40f89c..1292c468f5 100644 --- a/platform/web/js/libs/library_godot_input.js +++ b/platform/web/js/libs/library_godot_input.js @@ -29,6 +29,110 @@ /**************************************************************************/ /* + * IME API helper. + */ + +const GodotIME = { + $GodotIME__deps: ['$GodotRuntime', '$GodotEventListeners'], + $GodotIME__postset: 'GodotOS.atexit(function(resolve, reject) { GodotIME.clear(); resolve(); });', + $GodotIME: { + ime: null, + active: false, + + getModifiers: function (evt) { + return (evt.shiftKey + 0) + ((evt.altKey + 0) << 1) + ((evt.ctrlKey + 0) << 2) + ((evt.metaKey + 0) << 3); + }, + + ime_active: function (active) { + function focus_timer() { + GodotIME.active = true; + GodotIME.ime.focus(); + } + + if (GodotIME.ime) { + if (active) { + GodotIME.ime.style.display = 'block'; + setInterval(focus_timer, 100); + } else { + GodotIME.ime.style.display = 'none'; + GodotConfig.canvas.focus(); + GodotIME.active = false; + } + } + }, + + ime_position: function (x, y) { + if (GodotIME.ime) { + GodotIME.ime.style.left = `${x}px`; + GodotIME.ime.style.top = `${y}px`; + } + }, + + init: function (ime_cb, key_cb, code, key) { + function key_event_cb(pressed, evt) { + const modifiers = GodotIME.getModifiers(evt); + GodotRuntime.stringToHeap(evt.code, code, 32); + GodotRuntime.stringToHeap(evt.key, key, 32); + key_cb(pressed, evt.repeat, modifiers); + evt.preventDefault(); + } + function ime_event_cb(event) { + if (GodotIME.ime) { + if (event.type === 'compositionstart') { + ime_cb(0, null); + GodotIME.ime.innerHTML = ''; + } else if (event.type === 'compositionupdate') { + const ptr = GodotRuntime.allocString(event.data); + ime_cb(1, ptr); + GodotRuntime.free(ptr); + } else if (event.type === 'compositionend') { + const ptr = GodotRuntime.allocString(event.data); + ime_cb(2, ptr); + GodotRuntime.free(ptr); + GodotIME.ime.innerHTML = ''; + } + } + } + + const ime = document.createElement('div'); + ime.className = 'ime'; + ime.style.background = 'none'; + ime.style.opacity = 0.0; + ime.style.position = 'fixed'; + ime.style.left = '0px'; + ime.style.top = '0px'; + ime.style.width = '2px'; + ime.style.height = '2px'; + ime.style.display = 'none'; + ime.contentEditable = 'true'; + + GodotEventListeners.add(ime, 'compositionstart', ime_event_cb, false); + GodotEventListeners.add(ime, 'compositionupdate', ime_event_cb, false); + GodotEventListeners.add(ime, 'compositionend', ime_event_cb, false); + GodotEventListeners.add(ime, 'keydown', key_event_cb.bind(null, 1), false); + GodotEventListeners.add(ime, 'keyup', key_event_cb.bind(null, 0), false); + + ime.onblur = function () { + this.style.display = 'none'; + GodotConfig.canvas.focus(); + GodotIME.active = false; + }; + + GodotConfig.canvas.parentElement.appendChild(ime); + GodotIME.ime = ime; + }, + + clear: function () { + if (GodotIME.ime) { + GodotIME.ime.remove(); + GodotIME.ime = null; + } + }, + }, +}; +mergeInto(LibraryManager.library, GodotIME); + +/* * Gamepad API helper. */ const GodotInputGamepads = { @@ -338,7 +442,7 @@ mergeInto(LibraryManager.library, GodotInputDragDrop); * Godot exposed input functions. */ const GodotInput = { - $GodotInput__deps: ['$GodotRuntime', '$GodotConfig', '$GodotEventListeners', '$GodotInputGamepads', '$GodotInputDragDrop'], + $GodotInput__deps: ['$GodotRuntime', '$GodotConfig', '$GodotEventListeners', '$GodotInputGamepads', '$GodotInputDragDrop', '$GodotIME'], $GodotInput: { getModifiers: function (evt) { return (evt.shiftKey + 0) + ((evt.altKey + 0) << 1) + ((evt.ctrlKey + 0) << 2) + ((evt.metaKey + 0) << 3); @@ -462,6 +566,35 @@ const GodotInput = { }, /* + * IME API + */ + godot_js_set_ime_active__proxy: 'sync', + godot_js_set_ime_active__sig: 'vi', + godot_js_set_ime_active: function (p_active) { + GodotIME.ime_active(p_active); + }, + + godot_js_set_ime_position__proxy: 'sync', + godot_js_set_ime_position__sig: 'vii', + godot_js_set_ime_position: function (p_x, p_y) { + GodotIME.ime_position(p_x, p_y); + }, + + godot_js_set_ime_cb__proxy: 'sync', + godot_js_set_ime_cb__sig: 'viiii', + godot_js_set_ime_cb: function (p_ime_cb, p_key_cb, code, key) { + const ime_cb = GodotRuntime.get_func(p_ime_cb); + const key_cb = GodotRuntime.get_func(p_key_cb); + GodotIME.init(ime_cb, key_cb, code, key); + }, + + godot_js_is_ime_focused__proxy: 'sync', + godot_js_is_ime_focused__sig: 'i', + godot_js_is_ime_focused: function () { + return GodotIME.active; + }, + + /* * Gamepad API */ godot_js_input_gamepad_cb__proxy: 'sync', diff --git a/platform/web/js/libs/library_godot_webgl2.externs.js b/platform/web/js/libs/library_godot_webgl2.externs.js index 18d8d0815a..99190ab009 100644 --- a/platform/web/js/libs/library_godot_webgl2.externs.js +++ b/platform/web/js/libs/library_godot_webgl2.externs.js @@ -34,3 +34,19 @@ OVR_multiview2.prototype.FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR; * @return {void} */ OVR_multiview2.prototype.framebufferTextureMultiviewOVR = function(target, attachment, texture, level, baseViewIndex, numViews) {}; + +/** + * @constructor OCULUS_multiview + */ +function OCULUS_multiview() {} + +/** + * @param {number} target + * @param {number} attachment + * @param {WebGLTexture} texture + * @param {number} level + * @param {number} baseViewIndex + * @param {number} numViews + * @return {void} + */ +OCULUS_multiview.prototype.framebufferTextureMultisampleMultiviewOVR = function(target, attachment, texture, level, samples, baseViewIndex, numViews) {}; diff --git a/platform/web/js/libs/library_godot_webgl2.js b/platform/web/js/libs/library_godot_webgl2.js index dbaec9f01b..005b4b7917 100644 --- a/platform/web/js/libs/library_godot_webgl2.js +++ b/platform/web/js/libs/library_godot_webgl2.js @@ -61,6 +61,23 @@ const GodotWebGL2 = { const /** OVR_multiview2 */ ext = context.multiviewExt; ext.framebufferTextureMultiviewOVR(target, attachment, GL.textures[texture], level, base_view_index, num_views); }, + + godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR__deps: ['emscripten_webgl_get_current_context'], + godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR__proxy: 'sync', + godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR__sig: 'viiiiiii', + godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR: function (target, attachment, texture, level, samples, base_view_index, num_views) { + const context = GL.currentContext; + if (typeof context.oculusMultiviewExt === 'undefined') { + const /** OCULUS_multiview */ ext = context.GLctx.getExtension('OCULUS_multiview'); + if (!ext) { + GodotRuntime.error('Trying to call glFramebufferTextureMultisampleMultiviewOVR() without the OCULUS_multiview extension'); + return; + } + context.oculusMultiviewExt = ext; + } + const /** OCULUS_multiview */ ext = context.oculusMultiviewExt; + ext.framebufferTextureMultisampleMultiviewOVR(target, attachment, GL.textures[texture], level, samples, base_view_index, num_views); + }, }; autoAddDeps(GodotWebGL2, '$GodotWebGL2'); |