summaryrefslogtreecommitdiffstats
path: root/platform/web
diff options
context:
space:
mode:
Diffstat (limited to 'platform/web')
-rw-r--r--platform/web/detect.py14
-rw-r--r--platform/web/display_server_web.cpp153
-rw-r--r--platform/web/display_server_web.h28
-rw-r--r--platform/web/export/export_plugin.cpp17
-rw-r--r--platform/web/godot_js.h5
-rw-r--r--platform/web/godot_webgl2.h2
-rw-r--r--platform/web/js/libs/library_godot_input.js135
-rw-r--r--platform/web/js/libs/library_godot_webgl2.externs.js16
-rw-r--r--platform/web/js/libs/library_godot_webgl2.js17
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');