mirror of
https://github.com/godotengine/godot.git
synced 2025-01-24 03:24:32 -05:00
Merge pull request #40018 from Faless/js/more_improvements_4.0
HTML5 fixes, refactor, audio fallback, fixed FPS.
This commit is contained in:
commit
3fb5faaceb
8 changed files with 149 additions and 82 deletions
|
@ -36,6 +36,15 @@
|
||||||
|
|
||||||
AudioDriverJavaScript *AudioDriverJavaScript::singleton = nullptr;
|
AudioDriverJavaScript *AudioDriverJavaScript::singleton = nullptr;
|
||||||
|
|
||||||
|
bool AudioDriverJavaScript::is_available() {
|
||||||
|
return EM_ASM_INT({
|
||||||
|
if (!(window.AudioContext || window.webkitAudioContext)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
const char *AudioDriverJavaScript::get_name() const {
|
const char *AudioDriverJavaScript::get_name() const {
|
||||||
return "JavaScript";
|
return "JavaScript";
|
||||||
}
|
}
|
||||||
|
@ -207,12 +216,14 @@ void AudioDriverJavaScript::finish_async() {
|
||||||
|
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
EM_ASM({
|
EM_ASM({
|
||||||
var ref = Module.IDHandler.get($0);
|
const id = $0;
|
||||||
|
var ref = Module.IDHandler.get(id);
|
||||||
Module.async_finish.push(new Promise(function(accept, reject) {
|
Module.async_finish.push(new Promise(function(accept, reject) {
|
||||||
if (!ref) {
|
if (!ref) {
|
||||||
console.log("Ref not found!", $0, Module.IDHandler);
|
console.log("Ref not found!", id, Module.IDHandler);
|
||||||
setTimeout(accept, 0);
|
setTimeout(accept, 0);
|
||||||
} else {
|
} else {
|
||||||
|
Module.IDHandler.remove(id);
|
||||||
const context = ref['context'];
|
const context = ref['context'];
|
||||||
// Disconnect script and input.
|
// Disconnect script and input.
|
||||||
ref['script'].disconnect();
|
ref['script'].disconnect();
|
||||||
|
@ -226,7 +237,6 @@ void AudioDriverJavaScript::finish_async() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
Module.IDHandler.remove($0);
|
|
||||||
}, id);
|
}, id);
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
}
|
}
|
||||||
|
@ -293,9 +303,5 @@ Error AudioDriverJavaScript::capture_stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioDriverJavaScript::AudioDriverJavaScript() {
|
AudioDriverJavaScript::AudioDriverJavaScript() {
|
||||||
_driver_id = 0;
|
|
||||||
internal_buffer = nullptr;
|
|
||||||
buffer_length = 0;
|
|
||||||
|
|
||||||
singleton = this;
|
singleton = this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,12 +34,13 @@
|
||||||
#include "servers/audio_server.h"
|
#include "servers/audio_server.h"
|
||||||
|
|
||||||
class AudioDriverJavaScript : public AudioDriver {
|
class AudioDriverJavaScript : public AudioDriver {
|
||||||
float *internal_buffer;
|
float *internal_buffer = nullptr;
|
||||||
|
|
||||||
int _driver_id;
|
int _driver_id = 0;
|
||||||
int buffer_length;
|
int buffer_length = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static bool is_available();
|
||||||
void mix_to_js();
|
void mix_to_js();
|
||||||
void process_capture(float sample);
|
void process_capture(float sample);
|
||||||
|
|
||||||
|
|
|
@ -44,18 +44,15 @@
|
||||||
#define DOM_BUTTON_XBUTTON1 3
|
#define DOM_BUTTON_XBUTTON1 3
|
||||||
#define DOM_BUTTON_XBUTTON2 4
|
#define DOM_BUTTON_XBUTTON2 4
|
||||||
|
|
||||||
|
char DisplayServerJavaScript::canvas_id[256] = { 0 };
|
||||||
|
static bool cursor_inside_canvas = true;
|
||||||
|
|
||||||
DisplayServerJavaScript *DisplayServerJavaScript::get_singleton() {
|
DisplayServerJavaScript *DisplayServerJavaScript::get_singleton() {
|
||||||
return static_cast<DisplayServerJavaScript *>(DisplayServer::get_singleton());
|
return static_cast<DisplayServerJavaScript *>(DisplayServer::get_singleton());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Window (canvas)
|
// Window (canvas)
|
||||||
extern "C" EMSCRIPTEN_KEEPALIVE void _set_canvas_id(uint8_t *p_data, int p_data_size) {
|
void DisplayServerJavaScript::focus_canvas() {
|
||||||
DisplayServerJavaScript *display = DisplayServerJavaScript::get_singleton();
|
|
||||||
display->canvas_id.parse_utf8((const char *)p_data, p_data_size);
|
|
||||||
display->canvas_id = "#" + display->canvas_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void focus_canvas() {
|
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
EM_ASM(
|
EM_ASM(
|
||||||
Module['canvas'].focus();
|
Module['canvas'].focus();
|
||||||
|
@ -63,7 +60,7 @@ static void focus_canvas() {
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_canvas_focused() {
|
bool DisplayServerJavaScript::is_canvas_focused() {
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
return EM_ASM_INT_V(
|
return EM_ASM_INT_V(
|
||||||
return document.activeElement == Module['canvas'];
|
return document.activeElement == Module['canvas'];
|
||||||
|
@ -71,8 +68,21 @@ static bool is_canvas_focused() {
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
}
|
}
|
||||||
|
|
||||||
static Point2 compute_position_in_canvas(int x, int y) {
|
bool DisplayServerJavaScript::check_size_force_redraw() {
|
||||||
DisplayServerJavaScript *display = DisplayServerJavaScript::get_singleton();
|
int canvas_width;
|
||||||
|
int canvas_height;
|
||||||
|
emscripten_get_canvas_element_size(DisplayServerJavaScript::canvas_id, &canvas_width, &canvas_height);
|
||||||
|
if (last_width != canvas_width || last_height != canvas_height) {
|
||||||
|
last_width = canvas_width;
|
||||||
|
last_height = canvas_height;
|
||||||
|
// Update the framebuffer size and for redraw.
|
||||||
|
emscripten_set_canvas_element_size(DisplayServerJavaScript::canvas_id, canvas_width, canvas_height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point2 DisplayServerJavaScript::compute_position_in_canvas(int p_x, int p_y) {
|
||||||
int canvas_x = EM_ASM_INT({
|
int canvas_x = EM_ASM_INT({
|
||||||
return Module['canvas'].getBoundingClientRect().x;
|
return Module['canvas'].getBoundingClientRect().x;
|
||||||
});
|
});
|
||||||
|
@ -81,23 +91,22 @@ static Point2 compute_position_in_canvas(int x, int y) {
|
||||||
});
|
});
|
||||||
int canvas_width;
|
int canvas_width;
|
||||||
int canvas_height;
|
int canvas_height;
|
||||||
emscripten_get_canvas_element_size(display->canvas_id.utf8().get_data(), &canvas_width, &canvas_height);
|
emscripten_get_canvas_element_size(canvas_id, &canvas_width, &canvas_height);
|
||||||
|
|
||||||
double element_width;
|
double element_width;
|
||||||
double element_height;
|
double element_height;
|
||||||
emscripten_get_element_css_size(display->canvas_id.utf8().get_data(), &element_width, &element_height);
|
emscripten_get_element_css_size(canvas_id, &element_width, &element_height);
|
||||||
|
|
||||||
return Point2((int)(canvas_width / element_width * (x - canvas_x)),
|
return Point2((int)(canvas_width / element_width * (p_x - canvas_x)),
|
||||||
(int)(canvas_height / element_height * (y - canvas_y)));
|
(int)(canvas_height / element_height * (p_y - canvas_y)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cursor_inside_canvas = true;
|
|
||||||
|
|
||||||
EM_BOOL DisplayServerJavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) {
|
EM_BOOL DisplayServerJavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) {
|
||||||
DisplayServerJavaScript *display = get_singleton();
|
DisplayServerJavaScript *display = get_singleton();
|
||||||
// Empty ID is canvas.
|
// Empty ID is canvas.
|
||||||
String target_id = String::utf8(p_event->id);
|
String target_id = String::utf8(p_event->id);
|
||||||
if (target_id.empty() || "#" + target_id == display->canvas_id) {
|
String canvas_str_id = String::utf8(canvas_id);
|
||||||
|
if (target_id.empty() || target_id == canvas_str_id) {
|
||||||
// This event property is the only reliable data on
|
// This event property is the only reliable data on
|
||||||
// browser fullscreen state.
|
// browser fullscreen state.
|
||||||
if (p_event->isFullscreen) {
|
if (p_event->isFullscreen) {
|
||||||
|
@ -131,14 +140,14 @@ extern "C" EMSCRIPTEN_KEEPALIVE void _drop_files_callback(char *p_filev[], int p
|
||||||
// Keys
|
// Keys
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static void dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) {
|
void DisplayServerJavaScript::dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) {
|
||||||
godot_event->set_shift(emscripten_event_ptr->shiftKey);
|
godot_event->set_shift(emscripten_event_ptr->shiftKey);
|
||||||
godot_event->set_alt(emscripten_event_ptr->altKey);
|
godot_event->set_alt(emscripten_event_ptr->altKey);
|
||||||
godot_event->set_control(emscripten_event_ptr->ctrlKey);
|
godot_event->set_control(emscripten_event_ptr->ctrlKey);
|
||||||
godot_event->set_metakey(emscripten_event_ptr->metaKey);
|
godot_event->set_metakey(emscripten_event_ptr->metaKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Ref<InputEventKey> setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) {
|
Ref<InputEventKey> DisplayServerJavaScript::setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) {
|
||||||
Ref<InputEventKey> ev;
|
Ref<InputEventKey> ev;
|
||||||
ev.instance();
|
ev.instance();
|
||||||
ev->set_echo(emscripten_event->repeat);
|
ev->set_echo(emscripten_event->repeat);
|
||||||
|
@ -289,7 +298,7 @@ EM_BOOL DisplayServerJavaScript::mousemove_callback(int p_event_type, const Emsc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cursor
|
// Cursor
|
||||||
static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape) {
|
const char *DisplayServerJavaScript::godot2dom_cursor(DisplayServer::CursorShape p_shape) {
|
||||||
switch (p_shape) {
|
switch (p_shape) {
|
||||||
case DisplayServer::CURSOR_ARROW:
|
case DisplayServer::CURSOR_ARROW:
|
||||||
return "auto";
|
return "auto";
|
||||||
|
@ -330,7 +339,7 @@ static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_css_cursor(const char *p_cursor) {
|
void DisplayServerJavaScript::set_css_cursor(const char *p_cursor) {
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
EM_ASM_({
|
EM_ASM_({
|
||||||
Module['canvas'].style.cursor = UTF8ToString($0);
|
Module['canvas'].style.cursor = UTF8ToString($0);
|
||||||
|
@ -338,7 +347,7 @@ static void set_css_cursor(const char *p_cursor) {
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_css_cursor_hidden() {
|
bool DisplayServerJavaScript::is_css_cursor_hidden() const {
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
return EM_ASM_INT({
|
return EM_ASM_INT({
|
||||||
return Module['canvas'].style.cursor === 'none';
|
return Module['canvas'].style.cursor === 'none';
|
||||||
|
@ -820,23 +829,6 @@ DisplayServer *DisplayServerJavaScript::create_func(const String &p_rendering_dr
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
|
DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
|
||||||
/* clang-format off */
|
|
||||||
EM_ASM({
|
|
||||||
const canvas = Module['canvas'];
|
|
||||||
var enc = new TextEncoder("utf-8");
|
|
||||||
var buffer = new Uint8Array(enc.encode(canvas.id));
|
|
||||||
var len = buffer.byteLength;
|
|
||||||
var out = _malloc(len);
|
|
||||||
HEAPU8.set(buffer, out);
|
|
||||||
ccall("_set_canvas_id",
|
|
||||||
"void",
|
|
||||||
["number", "number"],
|
|
||||||
[out, len]
|
|
||||||
);
|
|
||||||
_free(out);
|
|
||||||
});
|
|
||||||
/* clang-format on */
|
|
||||||
|
|
||||||
RasterizerDummy::make_current(); // TODO GLES2 in Godot 4.0... or webgpu?
|
RasterizerDummy::make_current(); // TODO GLES2 in Godot 4.0... or webgpu?
|
||||||
#if 0
|
#if 0
|
||||||
EmscriptenWebGLContextAttributes attributes;
|
EmscriptenWebGLContextAttributes attributes;
|
||||||
|
@ -859,7 +851,7 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive
|
||||||
gl_initialization_error = true;
|
gl_initialization_error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(canvas_id.utf8().get_data(), &attributes);
|
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(canvas_id, &attributes);
|
||||||
if (emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS) {
|
if (emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS) {
|
||||||
gl_initialization_error = true;
|
gl_initialization_error = true;
|
||||||
}
|
}
|
||||||
|
@ -881,7 +873,6 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_RESULT result;
|
EMSCRIPTEN_RESULT result;
|
||||||
CharString id = canvas_id.utf8();
|
|
||||||
#define EM_CHECK(ev) \
|
#define EM_CHECK(ev) \
|
||||||
if (result != EMSCRIPTEN_RESULT_SUCCESS) \
|
if (result != EMSCRIPTEN_RESULT_SUCCESS) \
|
||||||
ERR_PRINT("Error while setting " #ev " callback: Code " + itos(result));
|
ERR_PRINT("Error while setting " #ev " callback: Code " + itos(result));
|
||||||
|
@ -895,16 +886,16 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive
|
||||||
// JavaScript APIs. For APIs that are not (sufficiently) exposed, EM_ASM
|
// JavaScript APIs. For APIs that are not (sufficiently) exposed, EM_ASM
|
||||||
// is used below.
|
// is used below.
|
||||||
SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, mousemove, mousemove_callback)
|
SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, mousemove, mousemove_callback)
|
||||||
SET_EM_CALLBACK(id.get_data(), mousedown, mouse_button_callback)
|
SET_EM_CALLBACK(canvas_id, mousedown, mouse_button_callback)
|
||||||
SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, mouseup, mouse_button_callback)
|
SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, mouseup, mouse_button_callback)
|
||||||
SET_EM_CALLBACK(id.get_data(), wheel, wheel_callback)
|
SET_EM_CALLBACK(canvas_id, wheel, wheel_callback)
|
||||||
SET_EM_CALLBACK(id.get_data(), touchstart, touch_press_callback)
|
SET_EM_CALLBACK(canvas_id, touchstart, touch_press_callback)
|
||||||
SET_EM_CALLBACK(id.get_data(), touchmove, touchmove_callback)
|
SET_EM_CALLBACK(canvas_id, touchmove, touchmove_callback)
|
||||||
SET_EM_CALLBACK(id.get_data(), touchend, touch_press_callback)
|
SET_EM_CALLBACK(canvas_id, touchend, touch_press_callback)
|
||||||
SET_EM_CALLBACK(id.get_data(), touchcancel, touch_press_callback)
|
SET_EM_CALLBACK(canvas_id, touchcancel, touch_press_callback)
|
||||||
SET_EM_CALLBACK(id.get_data(), keydown, keydown_callback)
|
SET_EM_CALLBACK(canvas_id, keydown, keydown_callback)
|
||||||
SET_EM_CALLBACK(id.get_data(), keypress, keypress_callback)
|
SET_EM_CALLBACK(canvas_id, keypress, keypress_callback)
|
||||||
SET_EM_CALLBACK(id.get_data(), keyup, keyup_callback)
|
SET_EM_CALLBACK(canvas_id, keyup, keyup_callback)
|
||||||
SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, fullscreenchange, fullscreen_change_callback)
|
SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, fullscreenchange, fullscreen_change_callback)
|
||||||
SET_EM_CALLBACK_NOTARGET(gamepadconnected, gamepad_change_callback)
|
SET_EM_CALLBACK_NOTARGET(gamepadconnected, gamepad_change_callback)
|
||||||
SET_EM_CALLBACK_NOTARGET(gamepaddisconnected, gamepad_change_callback)
|
SET_EM_CALLBACK_NOTARGET(gamepaddisconnected, gamepad_change_callback)
|
||||||
|
@ -1012,7 +1003,7 @@ Size2i DisplayServerJavaScript::screen_get_size(int p_screen) const {
|
||||||
|
|
||||||
Rect2i DisplayServerJavaScript::screen_get_usable_rect(int p_screen) const {
|
Rect2i DisplayServerJavaScript::screen_get_usable_rect(int p_screen) const {
|
||||||
int canvas[2];
|
int canvas[2];
|
||||||
emscripten_get_canvas_element_size(canvas_id.utf8().get_data(), canvas, canvas + 1);
|
emscripten_get_canvas_element_size(canvas_id, canvas, canvas + 1);
|
||||||
return Rect2i(0, 0, canvas[0], canvas[1]);
|
return Rect2i(0, 0, canvas[0], canvas[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1103,12 +1094,14 @@ Size2i DisplayServerJavaScript::window_get_min_size(WindowID p_window) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayServerJavaScript::window_set_size(const Size2i p_size, WindowID p_window) {
|
void DisplayServerJavaScript::window_set_size(const Size2i p_size, WindowID p_window) {
|
||||||
emscripten_set_canvas_element_size(canvas_id.utf8().get_data(), p_size.x, p_size.y);
|
last_width = p_size.x;
|
||||||
|
last_height = p_size.y;
|
||||||
|
emscripten_set_canvas_element_size(canvas_id, p_size.x, p_size.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
Size2i DisplayServerJavaScript::window_get_size(WindowID p_window) const {
|
Size2i DisplayServerJavaScript::window_get_size(WindowID p_window) const {
|
||||||
int canvas[2];
|
int canvas[2];
|
||||||
emscripten_get_canvas_element_size(canvas_id.utf8().get_data(), canvas, canvas + 1);
|
emscripten_get_canvas_element_size(canvas_id, canvas, canvas + 1);
|
||||||
return Size2(canvas[0], canvas[1]);
|
return Size2(canvas[0], canvas[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1134,7 +1127,7 @@ void DisplayServerJavaScript::window_set_mode(WindowMode p_mode, WindowID p_wind
|
||||||
strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
|
strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
|
||||||
strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
|
strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
|
||||||
strategy.canvasResizedCallback = nullptr;
|
strategy.canvasResizedCallback = nullptr;
|
||||||
EMSCRIPTEN_RESULT result = emscripten_request_fullscreen_strategy(canvas_id.utf8().get_data(), false, &strategy);
|
EMSCRIPTEN_RESULT result = emscripten_request_fullscreen_strategy(canvas_id, false, &strategy);
|
||||||
ERR_FAIL_COND_MSG(result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED, "Enabling fullscreen is only possible from an input callback for the HTML5 platform.");
|
ERR_FAIL_COND_MSG(result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED, "Enabling fullscreen is only possible from an input callback for the HTML5 platform.");
|
||||||
ERR_FAIL_COND_MSG(result != EMSCRIPTEN_RESULT_SUCCESS, "Enabling fullscreen is only possible from an input callback for the HTML5 platform.");
|
ERR_FAIL_COND_MSG(result != EMSCRIPTEN_RESULT_SUCCESS, "Enabling fullscreen is only possible from an input callback for the HTML5 platform.");
|
||||||
} break;
|
} break;
|
||||||
|
|
|
@ -53,6 +53,21 @@ class DisplayServerJavaScript : public DisplayServer {
|
||||||
double last_click_ms = 0;
|
double last_click_ms = 0;
|
||||||
int last_click_button_index = -1;
|
int last_click_button_index = -1;
|
||||||
|
|
||||||
|
int last_width = 0;
|
||||||
|
int last_height = 0;
|
||||||
|
|
||||||
|
// utilities
|
||||||
|
static Point2 compute_position_in_canvas(int p_x, int p_y);
|
||||||
|
static void focus_canvas();
|
||||||
|
static bool is_canvas_focused();
|
||||||
|
template <typename T>
|
||||||
|
static void dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event);
|
||||||
|
static Ref<InputEventKey> setup_key_event(const EmscriptenKeyboardEvent *emscripten_event);
|
||||||
|
static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape);
|
||||||
|
static void set_css_cursor(const char *p_cursor);
|
||||||
|
bool is_css_cursor_hidden() const;
|
||||||
|
|
||||||
|
// events
|
||||||
static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data);
|
static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data);
|
||||||
|
|
||||||
static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
|
static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
|
||||||
|
@ -81,17 +96,20 @@ protected:
|
||||||
public:
|
public:
|
||||||
// Override return type to make writing static callbacks less tedious.
|
// Override return type to make writing static callbacks less tedious.
|
||||||
static DisplayServerJavaScript *get_singleton();
|
static DisplayServerJavaScript *get_singleton();
|
||||||
|
static char canvas_id[256];
|
||||||
|
|
||||||
WindowMode window_mode = WINDOW_MODE_WINDOWED;
|
WindowMode window_mode = WINDOW_MODE_WINDOWED;
|
||||||
|
|
||||||
String clipboard;
|
String clipboard;
|
||||||
String canvas_id;
|
|
||||||
|
|
||||||
Callable window_event_callback;
|
Callable window_event_callback;
|
||||||
Callable input_event_callback;
|
Callable input_event_callback;
|
||||||
Callable input_text_callback;
|
Callable input_text_callback;
|
||||||
Callable drop_files_callback;
|
Callable drop_files_callback;
|
||||||
|
|
||||||
|
// utilities
|
||||||
|
bool check_size_force_redraw();
|
||||||
|
|
||||||
// from DisplayServer
|
// from DisplayServer
|
||||||
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
|
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
|
||||||
virtual bool has_feature(Feature p_feature) const;
|
virtual bool has_feature(Feature p_feature) const;
|
||||||
|
|
|
@ -30,11 +30,13 @@
|
||||||
|
|
||||||
#include "core/io/resource_loader.h"
|
#include "core/io/resource_loader.h"
|
||||||
#include "main/main.h"
|
#include "main/main.h"
|
||||||
#include "os_javascript.h"
|
#include "platform/javascript/display_server_javascript.h"
|
||||||
|
#include "platform/javascript/os_javascript.h"
|
||||||
|
|
||||||
#include <emscripten/emscripten.h>
|
#include <emscripten/emscripten.h>
|
||||||
|
|
||||||
static OS_JavaScript *os = nullptr;
|
static OS_JavaScript *os = nullptr;
|
||||||
|
static uint64_t target_ticks = 0;
|
||||||
|
|
||||||
void exit_callback() {
|
void exit_callback() {
|
||||||
emscripten_cancel_main_loop(); // After this, we can exit!
|
emscripten_cancel_main_loop(); // After this, we can exit!
|
||||||
|
@ -46,12 +48,32 @@ void exit_callback() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void main_loop_callback() {
|
void main_loop_callback() {
|
||||||
|
uint64_t current_ticks = os->get_ticks_usec();
|
||||||
|
|
||||||
|
bool force_draw = DisplayServerJavaScript::get_singleton()->check_size_force_redraw();
|
||||||
|
if (force_draw) {
|
||||||
|
Main::force_redraw();
|
||||||
|
} else if (current_ticks < target_ticks) {
|
||||||
|
return; // Skip frame.
|
||||||
|
}
|
||||||
|
|
||||||
|
int target_fps = Engine::get_singleton()->get_target_fps();
|
||||||
|
if (target_fps > 0) {
|
||||||
|
target_ticks += (uint64_t)(1000000 / target_fps);
|
||||||
|
}
|
||||||
if (os->main_loop_iterate()) {
|
if (os->main_loop_iterate()) {
|
||||||
emscripten_cancel_main_loop(); // Cancel current loop and wait for finalize_async.
|
emscripten_cancel_main_loop(); // Cancel current loop and wait for finalize_async.
|
||||||
|
/* clang-format off */
|
||||||
EM_ASM({
|
EM_ASM({
|
||||||
// This will contain the list of operations that need to complete before cleanup.
|
// This will contain the list of operations that need to complete before cleanup.
|
||||||
Module.async_finish = [];
|
Module.async_finish = [
|
||||||
|
// Always contains at least one async promise, to avoid firing immediately if nothing is added.
|
||||||
|
new Promise(function(accept, reject) {
|
||||||
|
setTimeout(accept, 0);
|
||||||
|
})
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
/* clang-format on */
|
||||||
os->get_main_loop()->finish();
|
os->get_main_loop()->finish();
|
||||||
os->finalize_async(); // Will add all the async finish functions.
|
os->finalize_async(); // Will add all the async finish functions.
|
||||||
EM_ASM({
|
EM_ASM({
|
||||||
|
@ -79,13 +101,35 @@ extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
|
||||||
ResourceLoader::set_abort_on_missing_resources(false);
|
ResourceLoader::set_abort_on_missing_resources(false);
|
||||||
Main::start();
|
Main::start();
|
||||||
os->get_main_loop()->init();
|
os->get_main_loop()->init();
|
||||||
emscripten_resume_main_loop();
|
|
||||||
// Immediately run the first iteration.
|
// Immediately run the first iteration.
|
||||||
// We are inside an animation frame, we want to immediately draw on the newly setup canvas.
|
// We are inside an animation frame, we want to immediately draw on the newly setup canvas.
|
||||||
main_loop_callback();
|
main_loop_callback();
|
||||||
|
emscripten_resume_main_loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
// Create and mount userfs immediately.
|
||||||
|
EM_ASM({
|
||||||
|
FS.mkdir('/userfs');
|
||||||
|
FS.mount(IDBFS, {}, '/userfs');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure locale.
|
||||||
|
char locale_ptr[16];
|
||||||
|
/* clang-format off */
|
||||||
|
EM_ASM({
|
||||||
|
stringToUTF8(Module['locale'], $0, 16);
|
||||||
|
}, locale_ptr);
|
||||||
|
/* clang-format on */
|
||||||
|
setenv("LANG", locale_ptr, true);
|
||||||
|
|
||||||
|
// Ensure the canvas ID.
|
||||||
|
/* clang-format off */
|
||||||
|
EM_ASM({
|
||||||
|
stringToUTF8("#" + Module['canvas'].id, $0, 255);
|
||||||
|
}, DisplayServerJavaScript::canvas_id);
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
os = new OS_JavaScript();
|
os = new OS_JavaScript();
|
||||||
Main::setup(argv[0], argc - 1, &argv[1], false);
|
Main::setup(argv[0], argc - 1, &argv[1], false);
|
||||||
emscripten_set_main_loop(main_loop_callback, -1, false);
|
emscripten_set_main_loop(main_loop_callback, -1, false);
|
||||||
|
@ -95,8 +139,6 @@ int main(int argc, char *argv[]) {
|
||||||
// run the 'main_after_fs_sync' function.
|
// run the 'main_after_fs_sync' function.
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
EM_ASM({
|
EM_ASM({
|
||||||
FS.mkdir('/userfs');
|
|
||||||
FS.mount(IDBFS, {}, '/userfs');
|
|
||||||
FS.syncfs(true, function(err) {
|
FS.syncfs(true, function(err) {
|
||||||
requestAnimationFrame(function() {
|
requestAnimationFrame(function() {
|
||||||
ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""]);
|
ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""]);
|
||||||
|
|
|
@ -74,18 +74,12 @@ void OS_JavaScript::initialize() {
|
||||||
EngineDebugger::register_uri_handler("ws://", RemoteDebuggerPeerWebSocket::create);
|
EngineDebugger::register_uri_handler("ws://", RemoteDebuggerPeerWebSocket::create);
|
||||||
EngineDebugger::register_uri_handler("wss://", RemoteDebuggerPeerWebSocket::create);
|
EngineDebugger::register_uri_handler("wss://", RemoteDebuggerPeerWebSocket::create);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char locale_ptr[16];
|
|
||||||
/* clang-format off */
|
|
||||||
EM_ASM({
|
|
||||||
stringToUTF8(Module['locale'], $0, 16);
|
|
||||||
}, locale_ptr);
|
|
||||||
/* clang-format on */
|
|
||||||
setenv("LANG", locale_ptr, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS_JavaScript::resume_audio() {
|
void OS_JavaScript::resume_audio() {
|
||||||
audio_driver_javascript.resume();
|
if (audio_driver_javascript) {
|
||||||
|
audio_driver_javascript->resume();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS_JavaScript::set_main_loop(MainLoop *p_main_loop) {
|
void OS_JavaScript::set_main_loop(MainLoop *p_main_loop) {
|
||||||
|
@ -133,11 +127,17 @@ void OS_JavaScript::delete_main_loop() {
|
||||||
|
|
||||||
void OS_JavaScript::finalize_async() {
|
void OS_JavaScript::finalize_async() {
|
||||||
finalizing = true;
|
finalizing = true;
|
||||||
audio_driver_javascript.finish_async();
|
if (audio_driver_javascript) {
|
||||||
|
audio_driver_javascript->finish_async();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS_JavaScript::finalize() {
|
void OS_JavaScript::finalize() {
|
||||||
delete_main_loop();
|
delete_main_loop();
|
||||||
|
if (audio_driver_javascript) {
|
||||||
|
memdelete(audio_driver_javascript);
|
||||||
|
audio_driver_javascript = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
|
@ -246,7 +246,10 @@ void OS_JavaScript::initialize_joypads() {
|
||||||
}
|
}
|
||||||
|
|
||||||
OS_JavaScript::OS_JavaScript() {
|
OS_JavaScript::OS_JavaScript() {
|
||||||
AudioDriverManager::add_driver(&audio_driver_javascript);
|
if (AudioDriverJavaScript::is_available()) {
|
||||||
|
audio_driver_javascript = memnew(AudioDriverJavaScript);
|
||||||
|
AudioDriverManager::add_driver(audio_driver_javascript);
|
||||||
|
}
|
||||||
|
|
||||||
Vector<Logger *> loggers;
|
Vector<Logger *> loggers;
|
||||||
loggers.push_back(memnew(StdLogger));
|
loggers.push_back(memnew(StdLogger));
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
|
|
||||||
class OS_JavaScript : public OS_Unix {
|
class OS_JavaScript : public OS_Unix {
|
||||||
MainLoop *main_loop = nullptr;
|
MainLoop *main_loop = nullptr;
|
||||||
AudioDriverJavaScript audio_driver_javascript;
|
AudioDriverJavaScript *audio_driver_javascript = nullptr;
|
||||||
|
|
||||||
bool finalizing = false;
|
bool finalizing = false;
|
||||||
bool idb_available = false;
|
bool idb_available = false;
|
||||||
|
@ -83,6 +83,9 @@ public:
|
||||||
String get_executable_path() const;
|
String get_executable_path() const;
|
||||||
virtual Error shell_open(String p_uri);
|
virtual Error shell_open(String p_uri);
|
||||||
virtual String get_name() const;
|
virtual String get_name() const;
|
||||||
|
// Override default OS implementation which would block the main thread with delay_usec.
|
||||||
|
// Implemented in javascript_main.cpp loop callback instead.
|
||||||
|
virtual void add_frame_delay(bool p_can_draw) {}
|
||||||
virtual bool can_draw() const;
|
virtual bool can_draw() const;
|
||||||
|
|
||||||
virtual String get_cache_path() const;
|
virtual String get_cache_path() const;
|
||||||
|
|
|
@ -184,6 +184,7 @@ void AudioDriverManager::initialize(int p_driver) {
|
||||||
GLOBAL_DEF_RST("audio/enable_audio_input", false);
|
GLOBAL_DEF_RST("audio/enable_audio_input", false);
|
||||||
GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE);
|
GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE);
|
||||||
GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
|
GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
|
||||||
|
GLOBAL_DEF_RST("audio/output_latency.web", 50); // Safer default output_latency for web.
|
||||||
|
|
||||||
int failed_driver = -1;
|
int failed_driver = -1;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue