diff options
Diffstat (limited to 'platform/javascript/display_server_javascript.cpp')
-rw-r--r-- | platform/javascript/display_server_javascript.cpp | 499 |
1 files changed, 205 insertions, 294 deletions
diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp index b43614eb99..e7564a599c 100644 --- a/platform/javascript/display_server_javascript.cpp +++ b/platform/javascript/display_server_javascript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,6 +30,9 @@ #include "platform/javascript/display_server_javascript.h" +#ifdef GLES3_ENABLED +#include "drivers/gles3/rasterizer_gles3.h" +#endif #include "platform/javascript/os_javascript.h" #include "servers/rendering/rasterizer_dummy.h" @@ -50,38 +53,17 @@ DisplayServerJavaScript *DisplayServerJavaScript::get_singleton() { } // Window (canvas) -void DisplayServerJavaScript::focus_canvas() { - godot_js_display_canvas_focus(); -} - -bool DisplayServerJavaScript::is_canvas_focused() { - return godot_js_display_canvas_is_focused() != 0; -} - bool DisplayServerJavaScript::check_size_force_redraw() { return godot_js_display_size_update() != 0; } -Point2 DisplayServerJavaScript::compute_position_in_canvas(int p_x, int p_y) { - int point[2]; - godot_js_display_compute_position(p_x, p_y, point, point + 1); - return Point2(point[0], point[1]); -} - -EM_BOOL DisplayServerJavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) { +void DisplayServerJavaScript::fullscreen_change_callback(int p_fullscreen) { DisplayServerJavaScript *display = get_singleton(); - // Empty ID is canvas. - String target_id = String::utf8(p_event->id); - if (target_id.is_empty() || target_id == String::utf8(&(display->canvas_id[1]))) { - // This event property is the only reliable data on - // browser fullscreen state. - if (p_event->isFullscreen) { - display->window_mode = WINDOW_MODE_FULLSCREEN; - } else { - display->window_mode = WINDOW_MODE_WINDOWED; - } + if (p_fullscreen) { + display->window_mode = WINDOW_MODE_FULLSCREEN; + } else { + display->window_mode = WINDOW_MODE_WINDOWED; } - return false; } // Drag and drop callback. @@ -118,137 +100,97 @@ void DisplayServerJavaScript::request_quit_callback() { // Keys -template <typename T> -void DisplayServerJavaScript::dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) { - godot_event->set_shift_pressed(emscripten_event_ptr->shiftKey); - godot_event->set_alt_pressed(emscripten_event_ptr->altKey); - godot_event->set_ctrl_pressed(emscripten_event_ptr->ctrlKey); - godot_event->set_meta_pressed(emscripten_event_ptr->metaKey); +void DisplayServerJavaScript::dom2godot_mod(Ref<InputEventWithModifiers> ev, int p_mod) { + ev->set_shift_pressed(p_mod & 1); + ev->set_alt_pressed(p_mod & 2); + ev->set_ctrl_pressed(p_mod & 4); + ev->set_meta_pressed(p_mod & 8); } -Ref<InputEventKey> DisplayServerJavaScript::setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) { +void DisplayServerJavaScript::key_callback(int p_pressed, int p_repeat, int p_modifiers) { + DisplayServerJavaScript *ds = get_singleton(); + JSKeyEvent &key_event = ds->key_event; + // Resume audio context after input in case autoplay was denied. + OS_JavaScript::get_singleton()->resume_audio(); + Ref<InputEventKey> ev; ev.instantiate(); - ev->set_echo(emscripten_event->repeat); - dom2godot_mod(emscripten_event, ev); - ev->set_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, false)); - ev->set_physical_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, true)); - - 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); - } + ev->set_echo(p_repeat); + ev->set_keycode(dom_code2godot_scancode(key_event.code, key_event.key, false)); + ev->set_physical_keycode(dom_code2godot_scancode(key_event.code, key_event.key, true)); + ev->set_pressed(p_pressed); + dom2godot_mod(ev, p_modifiers); + + String unicode = String::utf8(key_event.key); if (unicode.length() == 1) { ev->set_unicode(unicode[0]); } - - return ev; -} - -EM_BOOL DisplayServerJavaScript::keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - DisplayServerJavaScript *display = get_singleton(); - Ref<InputEventKey> ev = setup_key_event(p_event); - ev->set_pressed(true); - if (ev->get_unicode() == 0 && keycode_has_unicode(ev->get_keycode())) { - // Defer to keypress event for legacy unicode retrieval. - display->deferred_key_event = ev; - // Do not suppress keypress event. - return false; - } - Input::get_singleton()->parse_input_event(ev); - - // Make sure to flush all events so we can call restricted APIs inside the event. - Input::get_singleton()->flush_buffered_events(); - - return true; -} - -EM_BOOL DisplayServerJavaScript::keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - DisplayServerJavaScript *display = get_singleton(); - display->deferred_key_event->set_unicode(p_event->charCode); - Input::get_singleton()->parse_input_event(display->deferred_key_event); - - // Make sure to flush all events so we can call restricted APIs inside the event. - Input::get_singleton()->flush_buffered_events(); - - return true; -} - -EM_BOOL DisplayServerJavaScript::keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - Ref<InputEventKey> ev = setup_key_event(p_event); - ev->set_pressed(false); Input::get_singleton()->parse_input_event(ev); // Make sure to flush all events so we can call restricted APIs inside the event. Input::get_singleton()->flush_buffered_events(); - - return ev->get_keycode() != KEY_UNKNOWN && ev->get_keycode() != (Key)0; } // Mouse -EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) { - DisplayServerJavaScript *display = get_singleton(); +int DisplayServerJavaScript::mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers) { + DisplayServerJavaScript *ds = get_singleton(); + Point2 pos(p_x, p_y); + Input::get_singleton()->set_mouse_position(pos); Ref<InputEventMouseButton> ev; ev.instantiate(); - ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN); - ev->set_position(compute_position_in_canvas(p_event->clientX, p_event->clientY)); - ev->set_global_position(ev->get_position()); - dom2godot_mod(p_event, ev); + ev->set_position(pos); + ev->set_global_position(pos); + ev->set_pressed(p_pressed); + dom2godot_mod(ev, p_modifiers); - switch (p_event->button) { + switch (p_button) { case DOM_BUTTON_LEFT: - ev->set_button_index(MOUSE_BUTTON_LEFT); + ev->set_button_index(MouseButton::LEFT); break; case DOM_BUTTON_MIDDLE: - ev->set_button_index(MOUSE_BUTTON_MIDDLE); + ev->set_button_index(MouseButton::MIDDLE); break; case DOM_BUTTON_RIGHT: - ev->set_button_index(MOUSE_BUTTON_RIGHT); + ev->set_button_index(MouseButton::RIGHT); break; case DOM_BUTTON_XBUTTON1: - ev->set_button_index(MOUSE_BUTTON_XBUTTON1); + ev->set_button_index(MouseButton::MB_XBUTTON1); break; case DOM_BUTTON_XBUTTON2: - ev->set_button_index(MOUSE_BUTTON_XBUTTON2); + ev->set_button_index(MouseButton::MB_XBUTTON2); break; default: return false; } - if (ev->is_pressed()) { - double diff = emscripten_get_now() - display->last_click_ms; + if (p_pressed) { + uint64_t diff = (OS::get_singleton()->get_ticks_usec() / 1000) - ds->last_click_ms; - if (ev->get_button_index() == display->last_click_button_index) { - if (diff < 400 && Point2(display->last_click_pos).distance_to(ev->get_position()) < 5) { - display->last_click_ms = 0; - display->last_click_pos = Point2(-100, -100); - display->last_click_button_index = -1; + if (ev->get_button_index() == ds->last_click_button_index) { + if (diff < 400 && Point2(ds->last_click_pos).distance_to(ev->get_position()) < 5) { + ds->last_click_ms = 0; + ds->last_click_pos = Point2(-100, -100); + ds->last_click_button_index = MouseButton::NONE; ev->set_double_click(true); } } else { - display->last_click_button_index = ev->get_button_index(); + ds->last_click_button_index = ev->get_button_index(); } if (!ev->is_double_click()) { - display->last_click_ms += diff; - display->last_click_pos = ev->get_position(); + ds->last_click_ms += diff; + ds->last_click_pos = ev->get_position(); } } - Input *input = Input::get_singleton(); - int mask = input->get_mouse_button_mask(); - MouseButton button_flag = MouseButton(1 << (ev->get_button_index() - 1)); + MouseButton mask = Input::get_singleton()->get_mouse_button_mask(); + MouseButton button_flag = mouse_button_to_mask(ev->get_button_index()); if (ev->is_pressed()) { - // Since the event is consumed, focus manually. The containing iframe, - // if exists, may not have focus yet, so focus even if already focused. - focus_canvas(); mask |= button_flag; - } else if (mask & button_flag) { + } else if ((mask & button_flag) != MouseButton::NONE) { mask &= ~button_flag; } else { // Received release event, but press was outside the canvas, so ignore. @@ -256,7 +198,9 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E } ev->set_button_mask(mask); - input->parse_input_event(ev); + Input::get_singleton()->parse_input_event(ev); + // Resume audio context after input in case autoplay was denied. + OS_JavaScript::get_singleton()->resume_audio(); // Make sure to flush all events so we can call restricted APIs inside the event. Input::get_singleton()->flush_buffered_events(); @@ -266,31 +210,29 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E return true; } -EM_BOOL DisplayServerJavaScript::mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) { - DisplayServerJavaScript *ds = get_singleton(); - Input *input = Input::get_singleton(); - int input_mask = input->get_mouse_button_mask(); - Point2 pos = compute_position_in_canvas(p_event->clientX, p_event->clientY); +void DisplayServerJavaScript::mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers) { + MouseButton input_mask = Input::get_singleton()->get_mouse_button_mask(); // For motion outside the canvas, only read mouse movement if dragging // started inside the canvas; imitating desktop app behaviour. - if (!ds->cursor_inside_canvas && !input_mask) - return false; + if (!get_singleton()->cursor_inside_canvas && input_mask == MouseButton::NONE) { + return; + } + Point2 pos(p_x, p_y); + Input::get_singleton()->set_mouse_position(pos); Ref<InputEventMouseMotion> ev; ev.instantiate(); - dom2godot_mod(p_event, ev); + dom2godot_mod(ev, p_modifiers); ev->set_button_mask(input_mask); ev->set_position(pos); - ev->set_global_position(ev->get_position()); + ev->set_global_position(pos); - ev->set_relative(Vector2(p_event->movementX, p_event->movementY)); - input->set_mouse_position(ev->get_position()); - ev->set_speed(input->get_last_mouse_speed()); + ev->set_relative(Vector2(p_rel_x, p_rel_y)); + Input::get_singleton()->set_mouse_position(ev->get_position()); + ev->set_velocity(Input::get_singleton()->get_last_mouse_velocity()); - input->parse_input_event(ev); - // Don't suppress mouseover/-leave events. - return false; + Input::get_singleton()->parse_input_event(ev); } // Cursor @@ -430,17 +372,15 @@ void DisplayServerJavaScript::mouse_set_mode(MouseMode p_mode) { if (p_mode == MOUSE_MODE_VISIBLE) { godot_js_display_cursor_set_visible(1); - emscripten_exit_pointerlock(); + godot_js_display_cursor_lock_set(0); } else if (p_mode == MOUSE_MODE_HIDDEN) { godot_js_display_cursor_set_visible(0); - emscripten_exit_pointerlock(); + godot_js_display_cursor_lock_set(0); } else if (p_mode == MOUSE_MODE_CAPTURED) { godot_js_display_cursor_set_visible(1); - EMSCRIPTEN_RESULT result = emscripten_request_pointerlock(canvas_id, false); - ERR_FAIL_COND_MSG(result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED, "MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback."); - ERR_FAIL_COND_MSG(result != EMSCRIPTEN_RESULT_SUCCESS, "MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback."); + godot_js_display_cursor_lock_set(1); } } @@ -449,18 +389,21 @@ DisplayServer::MouseMode DisplayServerJavaScript::mouse_get_mode() const { return MOUSE_MODE_HIDDEN; } - EmscriptenPointerlockChangeEvent ev; - emscripten_get_pointerlock_status(&ev); - return (ev.isActive && String::utf8(ev.id) == String::utf8(&canvas_id[1])) ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE; + if (godot_js_display_cursor_is_locked()) { + return MOUSE_MODE_CAPTURED; + } + return MOUSE_MODE_VISIBLE; +} + +Point2i DisplayServerJavaScript::mouse_get_position() const { + return Input::get_singleton()->get_mouse_position(); } // Wheel -EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data) { - ERR_FAIL_COND_V(p_event_type != EMSCRIPTEN_EVENT_WHEEL, false); - DisplayServerJavaScript *ds = get_singleton(); - if (!is_canvas_focused()) { - if (ds->cursor_inside_canvas) { - focus_canvas(); +int DisplayServerJavaScript::mouse_wheel_callback(double p_delta_x, double p_delta_y) { + if (!godot_js_display_canvas_is_focused()) { + if (get_singleton()->cursor_inside_canvas) { + godot_js_display_canvas_focus(); } else { return false; } @@ -472,85 +415,78 @@ EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const Emscript ev->set_position(input->get_mouse_position()); ev->set_global_position(ev->get_position()); - ev->set_shift_pressed(input->is_key_pressed(KEY_SHIFT)); - ev->set_alt_pressed(input->is_key_pressed(KEY_ALT)); - ev->set_ctrl_pressed(input->is_key_pressed(KEY_CTRL)); - ev->set_meta_pressed(input->is_key_pressed(KEY_META)); - - if (p_event->deltaY < 0) - ev->set_button_index(MOUSE_BUTTON_WHEEL_UP); - else if (p_event->deltaY > 0) - ev->set_button_index(MOUSE_BUTTON_WHEEL_DOWN); - else if (p_event->deltaX > 0) - ev->set_button_index(MOUSE_BUTTON_WHEEL_LEFT); - else if (p_event->deltaX < 0) - ev->set_button_index(MOUSE_BUTTON_WHEEL_RIGHT); - else + ev->set_shift_pressed(input->is_key_pressed(Key::SHIFT)); + ev->set_alt_pressed(input->is_key_pressed(Key::ALT)); + ev->set_ctrl_pressed(input->is_key_pressed(Key::CTRL)); + ev->set_meta_pressed(input->is_key_pressed(Key::META)); + + if (p_delta_y < 0) { + ev->set_button_index(MouseButton::WHEEL_UP); + } else if (p_delta_y > 0) { + ev->set_button_index(MouseButton::WHEEL_DOWN); + } else if (p_delta_x > 0) { + ev->set_button_index(MouseButton::WHEEL_LEFT); + } else if (p_delta_x < 0) { + ev->set_button_index(MouseButton::WHEEL_RIGHT); + } else { return false; + } // Different browsers give wildly different delta values, and we can't // interpret deltaMode, so use default value for wheel events' factor. - int button_flag = 1 << (ev->get_button_index() - 1); + MouseButton button_flag = mouse_button_to_mask(ev->get_button_index()); ev->set_pressed(true); - ev->set_button_mask(MouseButton(input->get_mouse_button_mask() | button_flag)); + ev->set_button_mask(input->get_mouse_button_mask() | button_flag); input->parse_input_event(ev); - ev->set_pressed(false); - ev->set_button_mask(MouseButton(input->get_mouse_button_mask() & ~button_flag)); - input->parse_input_event(ev); + Ref<InputEventMouseButton> release = ev->duplicate(); + release->set_pressed(false); + release->set_button_mask(MouseButton(input->get_mouse_button_mask() & ~button_flag)); + input->parse_input_event(release); return true; } // Touch -EM_BOOL DisplayServerJavaScript::touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) { - DisplayServerJavaScript *display = get_singleton(); - Ref<InputEventScreenTouch> ev; - ev.instantiate(); - int lowest_id_index = -1; - for (int i = 0; i < p_event->numTouches; ++i) { - const EmscriptenTouchPoint &touch = p_event->touches[i]; - if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier) - lowest_id_index = i; - if (!touch.isChanged) - continue; - ev->set_index(touch.identifier); - ev->set_position(compute_position_in_canvas(touch.clientX, touch.clientY)); - display->touches[i] = ev->get_position(); - ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART); +void DisplayServerJavaScript::touch_callback(int p_type, int p_count) { + DisplayServerJavaScript *ds = get_singleton(); - Input::get_singleton()->parse_input_event(ev); - } + const JSTouchEvent &touch_event = ds->touch_event; + for (int i = 0; i < p_count; i++) { + Point2 point(touch_event.coords[i * 2], touch_event.coords[i * 2 + 1]); + if (p_type == 2) { + // touchmove + Ref<InputEventScreenDrag> ev; + ev.instantiate(); + ev->set_index(touch_event.identifier[i]); + ev->set_position(point); + + Point2 &prev = ds->touches[i]; + ev->set_relative(ev->get_position() - prev); + prev = ev->get_position(); + + Input::get_singleton()->parse_input_event(ev); + } else { + // touchstart/touchend + Ref<InputEventScreenTouch> ev; - // Make sure to flush all events so we can call restricted APIs inside the event. - Input::get_singleton()->flush_buffered_events(); + // Resume audio context after input in case autoplay was denied. + OS_JavaScript::get_singleton()->resume_audio(); - // Resume audio context after input in case autoplay was denied. - return true; -} + ev.instantiate(); + ev->set_index(touch_event.identifier[i]); + ev->set_position(point); + ev->set_pressed(p_type == 0); + ds->touches[i] = point; -EM_BOOL DisplayServerJavaScript::touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) { - DisplayServerJavaScript *display = get_singleton(); - Ref<InputEventScreenDrag> ev; - ev.instantiate(); - int lowest_id_index = -1; - for (int i = 0; i < p_event->numTouches; ++i) { - const EmscriptenTouchPoint &touch = p_event->touches[i]; - if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier) - lowest_id_index = i; - if (!touch.isChanged) - continue; - ev->set_index(touch.identifier); - ev->set_position(compute_position_in_canvas(touch.clientX, touch.clientY)); - Point2 &prev = display->touches[i]; - ev->set_relative(ev->get_position() - prev); - prev = ev->get_position(); + Input::get_singleton()->parse_input_event(ev); - Input::get_singleton()->parse_input_event(ev); + // Make sure to flush all events so we can call restricted APIs inside the event. + Input::get_singleton()->flush_buffered_events(); + } } - return true; } bool DisplayServerJavaScript::screen_is_touchscreen(int p_screen) const { @@ -564,7 +500,7 @@ void DisplayServerJavaScript::vk_input_text_callback(const char *p_text, int p_c return; } // Call input_text - Variant event = String(p_text); + Variant event = String::utf8(p_text); Variant *eventp = &event; Variant ret; Callable::CallError ce; @@ -576,12 +512,12 @@ void DisplayServerJavaScript::vk_input_text_callback(const char *p_text, int p_c k.instantiate(); k->set_pressed(true); k->set_echo(false); - k->set_keycode(KEY_RIGHT); + k->set_keycode(Key::RIGHT); input->parse_input_event(k); k.instantiate(); k->set_pressed(false); k->set_echo(false); - k->set_keycode(KEY_RIGHT); + k->set_keycode(Key::RIGHT); input->parse_input_event(k); } } @@ -594,6 +530,10 @@ void DisplayServerJavaScript::virtual_keyboard_hide() { godot_js_display_vk_hide(); } +void DisplayServerJavaScript::window_blur_callback() { + Input::get_singleton()->release_pressed_events(); +} + // Gamepad void DisplayServerJavaScript::gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid) { Input *input = Input::get_singleton(); @@ -606,49 +546,43 @@ void DisplayServerJavaScript::gamepad_callback(int p_index, int p_connected, con void DisplayServerJavaScript::process_joypads() { Input *input = Input::get_singleton(); - int32_t pads = godot_js_display_gamepad_sample_count(); + int32_t pads = godot_js_input_gamepad_sample_count(); int32_t s_btns_num = 0; int32_t s_axes_num = 0; int32_t s_standard = 0; float s_btns[16]; float s_axes[10]; for (int idx = 0; idx < pads; idx++) { - int err = godot_js_display_gamepad_sample_get(idx, s_btns, &s_btns_num, s_axes, &s_axes_num, &s_standard); + int err = godot_js_input_gamepad_sample_get(idx, s_btns, &s_btns_num, s_axes, &s_axes_num, &s_standard); if (err) { continue; } for (int b = 0; b < s_btns_num; b++) { - float value = s_btns[b]; // Buttons 6 and 7 in the standard mapping need to be - // axis to be handled as JOY_AXIS_TRIGGER by Godot. + // axis to be handled as JoyAxis::TRIGGER by Godot. if (s_standard && (b == 6 || b == 7)) { - Input::JoyAxisValue joy_axis; - joy_axis.min = 0; - joy_axis.value = value; - JoyAxis a = b == 6 ? JOY_AXIS_TRIGGER_LEFT : JOY_AXIS_TRIGGER_RIGHT; - input->joy_axis(idx, a, joy_axis); + input->joy_axis(idx, (JoyAxis)b, s_btns[b]); } else { - input->joy_button(idx, (JoyButton)b, value); + input->joy_button(idx, (JoyButton)b, s_btns[b]); } } for (int a = 0; a < s_axes_num; a++) { - Input::JoyAxisValue joy_axis; - joy_axis.min = -1; - joy_axis.value = s_axes[a]; - input->joy_axis(idx, (JoyAxis)a, joy_axis); + input->joy_axis(idx, (JoyAxis)a, s_axes[a]); } } } Vector<String> DisplayServerJavaScript::get_rendering_drivers_func() { Vector<String> drivers; - drivers.push_back("dummy"); +#ifdef GLES3_ENABLED + drivers.push_back("opengl3"); +#endif return drivers; } // Clipboard void DisplayServerJavaScript::update_clipboard_callback(const char *p_text) { - get_singleton()->clipboard = p_text; + get_singleton()->clipboard = String::utf8(p_text); } void DisplayServerJavaScript::clipboard_set(const String &p_text) { @@ -711,11 +645,6 @@ void DisplayServerJavaScript::set_icon(const Ref<Image> &p_icon) { } void DisplayServerJavaScript::_dispatch_input_event(const Ref<InputEvent> &p_event) { - OS_JavaScript *os = OS_JavaScript::get_singleton(); - - // Resume audio context after input in case autoplay was denied. - os->resume_audio(); - Callable cb = get_singleton()->input_event_callback; if (!cb.is_null()) { Variant ev = p_event; @@ -745,92 +674,70 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive // Expose method for requesting quit. godot_js_os_request_quit_cb(request_quit_callback); - RasterizerDummy::make_current(); // TODO GLES2 in Godot 4.0... or webgpu? -#if 0 - EmscriptenWebGLContextAttributes attributes; - emscripten_webgl_init_context_attributes(&attributes); - attributes.alpha = GLOBAL_GET("display/window/per_pixel_transparency/allowed"); - attributes.antialias = false; - ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER); - - if (p_desired.layered) { - set_window_per_pixel_transparency_enabled(true); - } - - bool gl_initialization_error = false; - - if (RasterizerGLES2::is_viable() == OK) { - attributes.majorVersion = 1; - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - } else { - gl_initialization_error = true; - } - - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(canvas_id, &attributes); - if (emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS) { - gl_initialization_error = true; +#ifdef GLES3_ENABLED + // TODO "vulkan" defaults to webgl2 for now. + bool wants_webgl2 = p_rendering_driver == "opengl3" || p_rendering_driver == "vulkan"; + bool webgl2_init_failed = wants_webgl2 && !godot_js_display_has_webgl(2); + if (wants_webgl2 && !webgl2_init_failed) { + EmscriptenWebGLContextAttributes attributes; + emscripten_webgl_init_context_attributes(&attributes); + //attributes.alpha = GLOBAL_GET("display/window/per_pixel_transparency/allowed"); + attributes.alpha = true; + attributes.antialias = false; + attributes.majorVersion = 2; + + webgl_ctx = emscripten_webgl_create_context(canvas_id, &attributes); + if (emscripten_webgl_make_context_current(webgl_ctx) != EMSCRIPTEN_RESULT_SUCCESS) { + webgl2_init_failed = true; + } else { + RasterizerGLES3::make_current(); + } } - - if (gl_initialization_error) { - OS::get_singleton()->alert("Your browser does not seem to support WebGL. Please update your browser version.", + if (webgl2_init_failed) { + OS::get_singleton()->alert("Your browser does not seem to support WebGL2. Please update your browser version.", "Unable to initialize video driver"); - return ERR_UNAVAILABLE; } - - video_driver_index = p_video_driver; + if (!wants_webgl2 || webgl2_init_failed) { + RasterizerDummy::make_current(); + } +#else + RasterizerDummy::make_current(); #endif - EMSCRIPTEN_RESULT result; -#define EM_CHECK(ev) \ - if (result != EMSCRIPTEN_RESULT_SUCCESS) \ - ERR_PRINT("Error while setting " #ev " callback: Code " + itos(result)); -#define SET_EM_CALLBACK(target, ev, cb) \ - result = emscripten_set_##ev##_callback(target, nullptr, true, &cb); \ - EM_CHECK(ev) -#define SET_EM_WINDOW_CALLBACK(ev, cb) \ - result = emscripten_set_##ev##_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, &cb); \ - EM_CHECK(ev) - // These callbacks from Emscripten's html5.h suffice to access most - // JavaScript APIs. - SET_EM_CALLBACK(canvas_id, mousedown, mouse_button_callback) - SET_EM_WINDOW_CALLBACK(mousemove, mousemove_callback) - SET_EM_WINDOW_CALLBACK(mouseup, mouse_button_callback) - SET_EM_CALLBACK(canvas_id, wheel, wheel_callback) - SET_EM_CALLBACK(canvas_id, touchstart, touch_press_callback) - SET_EM_CALLBACK(canvas_id, touchmove, touchmove_callback) - SET_EM_CALLBACK(canvas_id, touchend, touch_press_callback) - SET_EM_CALLBACK(canvas_id, touchcancel, touch_press_callback) - SET_EM_CALLBACK(canvas_id, keydown, keydown_callback) - SET_EM_CALLBACK(canvas_id, keypress, keypress_callback) - SET_EM_CALLBACK(canvas_id, keyup, keyup_callback) - SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, fullscreenchange, fullscreen_change_callback) -#undef SET_EM_CALLBACK -#undef EM_CHECK - - // For APIs that are not (sufficiently) exposed, a - // library is used below (implemented in library_godot_display.js). + // JS Input interface (js/libs/library_godot_input.js) + godot_js_input_mouse_button_cb(&DisplayServerJavaScript::mouse_button_callback); + godot_js_input_mouse_move_cb(&DisplayServerJavaScript::mouse_move_callback); + godot_js_input_mouse_wheel_cb(&DisplayServerJavaScript::mouse_wheel_callback); + godot_js_input_touch_cb(&DisplayServerJavaScript::touch_callback, touch_event.identifier, touch_event.coords); + godot_js_input_key_cb(&DisplayServerJavaScript::key_callback, key_event.code, key_event.key); + godot_js_input_paste_cb(update_clipboard_callback); + godot_js_input_drop_files_cb(drop_files_js_callback); + godot_js_input_gamepad_cb(&DisplayServerJavaScript::gamepad_callback); + + // JS Display interface (js/libs/library_godot_display.js) + godot_js_display_fullscreen_cb(&DisplayServerJavaScript::fullscreen_change_callback); + godot_js_display_window_blur_cb(&window_blur_callback); godot_js_display_notification_cb(&send_window_event_callback, WINDOW_EVENT_MOUSE_ENTER, WINDOW_EVENT_MOUSE_EXIT, WINDOW_EVENT_FOCUS_IN, WINDOW_EVENT_FOCUS_OUT); - godot_js_display_paste_cb(update_clipboard_callback); - godot_js_display_drop_files_cb(drop_files_js_callback); - godot_js_display_gamepad_cb(&DisplayServerJavaScript::gamepad_callback); godot_js_display_vk_cb(&vk_input_text_callback); Input::get_singleton()->set_event_dispatch_function(_dispatch_input_event); } DisplayServerJavaScript::~DisplayServerJavaScript() { - //emscripten_webgl_commit_frame(); - //emscripten_webgl_destroy_context(webgl_ctx); +#ifdef GLES3_ENABLED + if (webgl_ctx) { + emscripten_webgl_commit_frame(); + emscripten_webgl_destroy_context(webgl_ctx); + } +#endif } bool DisplayServerJavaScript::has_feature(Feature p_feature) const { switch (p_feature) { - //case FEATURE_CONSOLE_WINDOW: //case FEATURE_GLOBAL_MENU: //case FEATURE_HIDPI: //case FEATURE_IME: @@ -1040,7 +947,7 @@ bool DisplayServerJavaScript::can_any_window_draw() const { void DisplayServerJavaScript::process_events() { Input::get_singleton()->flush_buffered_events(); - if (godot_js_display_gamepad_sample() == OK) { + if (godot_js_input_gamepad_sample() == OK) { process_joypads(); } } @@ -1054,5 +961,9 @@ bool DisplayServerJavaScript::get_swap_cancel_ok() { } void DisplayServerJavaScript::swap_buffers() { - //emscripten_webgl_commit_frame(); +#ifdef GLES3_ENABLED + if (webgl_ctx) { + emscripten_webgl_commit_frame(); + } +#endif } |