From b7ac3c1aeb52f9136e2bf4237740d7d6eeb69be7 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Fri, 10 Sep 2021 21:46:22 +0200 Subject: [PATCH] [HTML5] Implement mouse/touch/key events in JS library. This makes us more independent from emscripten libraries, giving us more control on the application lifecycle. --- platform/javascript/.eslintrc.libs.js | 1 + platform/javascript/SCsub | 1 + platform/javascript/godot_js.h | 6 +- .../js/libs/library_godot_display.js | 9 - .../javascript/js/libs/library_godot_input.js | 133 +++++++++++ platform/javascript/os_javascript.cpp | 218 ++++++------------ platform/javascript/os_javascript.h | 33 +-- 7 files changed, 233 insertions(+), 168 deletions(-) create mode 100644 platform/javascript/js/libs/library_godot_input.js diff --git a/platform/javascript/.eslintrc.libs.js b/platform/javascript/.eslintrc.libs.js index 81b1b8c8646..a46be42350c 100644 --- a/platform/javascript/.eslintrc.libs.js +++ b/platform/javascript/.eslintrc.libs.js @@ -15,6 +15,7 @@ module.exports = { "IDBFS": true, "GodotOS": true, "GodotConfig": true, + "GodotDisplayListeners": true, "GodotRuntime": true, "GodotFS": true, "IDHandler": true, diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index 4403a1ebd9e..0385ee1ec6e 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -19,6 +19,7 @@ sys_env.AddJSLibraries( "js/libs/library_godot_fetch.js", "js/libs/library_godot_os.js", "js/libs/library_godot_runtime.js", + "js/libs/library_godot_input.js", ] ) diff --git a/platform/javascript/godot_js.h b/platform/javascript/godot_js.h index d332af2c311..512349180fd 100644 --- a/platform/javascript/godot_js.h +++ b/platform/javascript/godot_js.h @@ -68,7 +68,6 @@ extern void godot_js_display_window_size_get(int32_t *p_x, int32_t *p_y); extern void godot_js_display_screen_size_get(int32_t *p_x, int32_t *p_y); extern int godot_js_display_fullscreen_request(); extern int godot_js_display_fullscreen_exit(); -extern void godot_js_display_compute_position(int p_x, int p_y, int32_t *r_x, int32_t *r_y); extern void godot_js_display_window_title_set(const char *p_text); extern void godot_js_display_window_icon_set(const uint8_t *p_ptr, int p_len); extern int godot_js_display_has_webgl(int p_version); @@ -90,6 +89,11 @@ extern int godot_js_display_gamepad_sample_count(); extern int godot_js_display_gamepad_sample_get(int p_idx, float r_btns[16], int32_t *r_btns_num, float r_axes[10], int32_t *r_axes_num, int32_t *r_standard); // Display listeners +extern void godot_js_display_mouse_button_cb(int (*p_callback)(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers)); +extern void godot_js_display_mouse_move_cb(void (*p_callback)(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers)); +extern void godot_js_display_mouse_wheel_cb(int (*p_callback)(double p_delta_x, double p_delta_y)); +extern void godot_js_display_touch_cb(void (*p_callback)(int p_type, int p_count), uint32_t *r_identifiers, double *r_coords); +extern void godot_js_display_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_display_notification_cb(void (*p_callback)(int p_notification), int p_enter, int p_exit, int p_in, int p_out); extern void godot_js_display_paste_cb(void (*p_callback)(const char *p_text)); extern void godot_js_display_drop_files_cb(void (*p_callback)(char **p_filev, int p_filec)); diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js index affae903705..0ad52a690fe 100644 --- a/platform/javascript/js/libs/library_godot_display.js +++ b/platform/javascript/js/libs/library_godot_display.js @@ -710,15 +710,6 @@ const GodotDisplay = { GodotRuntime.setHeapValue(p_height, GodotConfig.canvas.height, 'i32'); }, - godot_js_display_compute_position: function (x, y, r_x, r_y) { - const canvas = GodotConfig.canvas; - const rect = canvas.getBoundingClientRect(); - const rw = canvas.width / rect.width; - const rh = canvas.height / rect.height; - GodotRuntime.setHeapValue(r_x, (x - rect.x) * rw, 'i32'); - GodotRuntime.setHeapValue(r_y, (y - rect.y) * rh, 'i32'); - }, - godot_js_display_has_webgl__sig: 'ii', godot_js_display_has_webgl: function (p_version) { if (p_version !== 1 && p_version !== 2) { diff --git a/platform/javascript/js/libs/library_godot_input.js b/platform/javascript/js/libs/library_godot_input.js new file mode 100644 index 00000000000..279aa8f012c --- /dev/null +++ b/platform/javascript/js/libs/library_godot_input.js @@ -0,0 +1,133 @@ +/*************************************************************************/ +/* library_godot_input.js */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +const GodotInput = { + $GodotInput__deps: ['$GodotRuntime', '$GodotConfig', '$GodotDisplayListeners'], + $GodotInput: { + getModifiers: function (evt) { + return (evt.shiftKey + 0) + ((evt.altKey + 0) << 1) + ((evt.ctrlKey + 0) << 2) + ((evt.metaKey + 0) << 3); + }, + computePosition: function (evt, rect) { + const canvas = GodotConfig.canvas; + const rw = canvas.width / rect.width; + const rh = canvas.height / rect.height; + const x = (evt.clientX - rect.x) * rw; + const y = (evt.clientY - rect.y) * rh; + return [x, y]; + }, + }, + + godot_js_display_mouse_move_cb__sig: 'vi', + godot_js_display_mouse_move_cb: function (callback) { + const func = GodotRuntime.get_func(callback); + const canvas = GodotConfig.canvas; + function move_cb(evt) { + const rect = canvas.getBoundingClientRect(); + const pos = GodotInput.computePosition(evt, rect); + // Scale movement + const rw = canvas.width / rect.width; + const rh = canvas.height / rect.height; + const rel_pos_x = evt.movementX * rw; + const rel_pos_y = evt.movementY * rh; + const modifiers = GodotInput.getModifiers(evt); + func(pos[0], pos[1], rel_pos_x, rel_pos_y, modifiers); + } + GodotDisplayListeners.add(window, 'mousemove', move_cb, false); + }, + + godot_js_display_mouse_wheel_cb__sig: 'vi', + godot_js_display_mouse_wheel_cb: function (callback) { + const func = GodotRuntime.get_func(callback); + function wheel_cb(evt) { + if (func(evt['deltaX'] || 0, evt['deltaY'] || 0)) { + evt.preventDefault(); + } + } + GodotDisplayListeners.add(GodotConfig.canvas, 'wheel', wheel_cb, false); + }, + + godot_js_display_mouse_button_cb__sig: 'vi', + godot_js_display_mouse_button_cb: function (callback) { + const func = GodotRuntime.get_func(callback); + const canvas = GodotConfig.canvas; + function button_cb(p_pressed, evt) { + const rect = canvas.getBoundingClientRect(); + const pos = GodotInput.computePosition(evt, rect); + const modifiers = GodotInput.getModifiers(evt); + if (func(p_pressed, evt.button, pos[0], pos[1], modifiers)) { + evt.preventDefault(); + } + } + GodotDisplayListeners.add(canvas, 'mousedown', button_cb.bind(null, 1), false); + GodotDisplayListeners.add(window, 'mouseup', button_cb.bind(null, 0), false); + }, + + godot_js_display_touch_cb__sig: 'viii', + godot_js_display_touch_cb: function (callback, ids, coords) { + const func = GodotRuntime.get_func(callback); + const canvas = GodotConfig.canvas; + function touch_cb(type, evt) { + const rect = canvas.getBoundingClientRect(); + const touches = evt.changedTouches; + for (let i = 0; i < touches.length; i++) { + const touch = touches[i]; + const pos = GodotInput.computePosition(touch, rect); + GodotRuntime.setHeapValue(coords + (i * 2), pos[0], 'double'); + GodotRuntime.setHeapValue(coords + (i * 2 + 8), pos[1], 'double'); + GodotRuntime.setHeapValue(ids + i, touch.identifier, 'i32'); + } + func(type, touches.length); + if (evt.cancelable) { + evt.preventDefault(); + } + } + GodotDisplayListeners.add(canvas, 'touchstart', touch_cb.bind(null, 0), false); + GodotDisplayListeners.add(canvas, 'touchend', touch_cb.bind(null, 1), false); + GodotDisplayListeners.add(canvas, 'touchcancel', touch_cb.bind(null, 1), false); + GodotDisplayListeners.add(canvas, 'touchmove', touch_cb.bind(null, 2), false); + }, + + godot_js_display_key_cb__sig: 'viii', + godot_js_display_key_cb: function (callback, code, key) { + const func = GodotRuntime.get_func(callback); + function key_cb(pressed, evt) { + const modifiers = GodotInput.getModifiers(evt); + GodotRuntime.stringToHeap(evt.code, code, 32); + GodotRuntime.stringToHeap(evt.key, key, 32); + func(pressed, evt.repeat, modifiers); + evt.preventDefault(); + } + GodotDisplayListeners.add(GodotConfig.canvas, 'keydown', key_cb.bind(null, 1), false); + GodotDisplayListeners.add(GodotConfig.canvas, 'keyup', key_cb.bind(null, 0), false); + }, +}; + +autoAddDeps(GodotInput, '$GodotInput'); +mergeInto(LibraryManager.library, GodotInput); diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 033260cd1fd..908ce836998 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -92,12 +92,6 @@ void OS_JavaScript::send_notification_callback(int p_notification) { // Window (canvas) -Point2 OS_JavaScript::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]); -} - bool OS_JavaScript::check_size_force_redraw() { return godot_js_display_size_update() != 0; } @@ -232,76 +226,35 @@ void OS_JavaScript::set_window_per_pixel_transparency_enabled(bool p_enabled) { // Keys -template -static void dom2godot_mod(T *emscripten_event_ptr, Ref godot_event) { - godot_event->set_shift(emscripten_event_ptr->shiftKey); - godot_event->set_alt(emscripten_event_ptr->altKey); - godot_event->set_control(emscripten_event_ptr->ctrlKey); - godot_event->set_metakey(emscripten_event_ptr->metaKey); +static void dom2godot_mod(Ref ev, int p_mod) { + ev->set_shift(p_mod & 1); + ev->set_alt(p_mod & 2); + ev->set_control(p_mod & 4); + ev->set_metakey(p_mod & 8); } -static Ref setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) { +void OS_JavaScript::key_callback(int p_pressed, int p_repeat, int p_modifiers) { + OS_JavaScript *os = get_singleton(); + JSKeyEvent &key_event = os->key_event; + // Resume audio context after input in case autoplay was denied. + os->resume_audio(); + Ref ev; ev.instance(); - ev->set_echo(emscripten_event->repeat); - dom2godot_mod(emscripten_event, ev); - ev->set_scancode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, false)); - ev->set_physical_scancode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, true)); + ev->set_echo(p_repeat); + ev->set_scancode(dom_code2godot_scancode(key_event.code, key_event.key, false)); + ev->set_physical_scancode(dom_code2godot_scancode(key_event.code, key_event.key, true)); + ev->set_pressed(p_pressed); + dom2godot_mod(ev, p_modifiers); - 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); - } + String unicode = String::utf8(key_event.key); if (unicode.length() == 1) { ev->set_unicode(unicode[0]); } - - return ev; -} - -EM_BOOL OS_JavaScript::keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - OS_JavaScript *os = get_singleton(); - Ref ev = setup_key_event(p_event); - ev->set_pressed(true); - if (ev->get_unicode() == 0 && keycode_has_unicode(ev->get_scancode())) { - // Defer to keypress event for legacy unicode retrieval. - os->deferred_key_event = ev; - // Do not suppress keypress event. - return false; - } os->input->parse_input_event(ev); // Make sure to flush all events so we can call restricted APIs inside the event. os->input->flush_buffered_events(); - - // Resume audio context after input in case autoplay was denied. - os->resume_audio(); - return true; -} - -EM_BOOL OS_JavaScript::keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - OS_JavaScript *os = get_singleton(); - os->deferred_key_event->set_unicode(p_event->charCode); - os->input->parse_input_event(os->deferred_key_event); - - // Make sure to flush all events so we can call restricted APIs inside the event. - os->input->flush_buffered_events(); - - return true; -} - -EM_BOOL OS_JavaScript::keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - OS_JavaScript *os = get_singleton(); - Ref ev = setup_key_event(p_event); - ev->set_pressed(false); - os->input->parse_input_event(ev); - - // Make sure to flush all events so we can call restricted APIs inside the event. - os->input->flush_buffered_events(); - - return ev->get_scancode() != KEY_UNKNOWN && ev->get_scancode() != 0; } // Mouse @@ -314,17 +267,18 @@ int OS_JavaScript::get_mouse_button_state() const { return input->get_mouse_button_mask(); } -EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) { +int OS_JavaScript::mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers) { OS_JavaScript *os = get_singleton(); Ref ev; ev.instance(); - ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN); - ev->set_position(compute_position_in_canvas(p_event->clientX, p_event->clientY)); + ev->set_pressed(p_pressed); + ev->set_position(Point2(p_x, p_y)); ev->set_global_position(ev->get_position()); - dom2godot_mod(p_event, ev); + 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(BUTTON_LEFT); break; @@ -344,8 +298,8 @@ EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenM return false; } - if (ev->is_pressed()) { - double diff = emscripten_get_now() - os->last_click_ms; + if (p_pressed) { + uint64_t diff = (OS::get_singleton()->get_ticks_usec() / 1000) - os->last_click_ms; if (ev->get_button_index() == os->last_click_button_index) { if (diff < 400 && Point2(os->last_click_pos).distance_to(ev->get_position()) < 5) { @@ -368,9 +322,6 @@ EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenM int mask = os->input->get_mouse_button_mask(); int button_flag = 1 << (ev->get_button_index() - 1); 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. - godot_js_display_canvas_focus(); mask |= button_flag; } else if (mask & button_flag) { mask &= ~button_flag; @@ -381,43 +332,39 @@ EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenM ev->set_button_mask(mask); os->input->parse_input_event(ev); + // Resume audio context after input in case autoplay was denied. + os->resume_audio(); // Make sure to flush all events so we can call restricted APIs inside the event. os->input->flush_buffered_events(); - // Resume audio context after input in case autoplay was denied. - os->resume_audio(); - // Prevent multi-click text selection and wheel-click scrolling anchor. // Context menu is prevented through contextmenu event. return true; } -EM_BOOL OS_JavaScript::mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) { +void OS_JavaScript::mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers) { OS_JavaScript *os = get_singleton(); int input_mask = os->input->get_mouse_button_mask(); - Point2 pos = compute_position_in_canvas(p_event->clientX, p_event->clientY); // For motion outside the canvas, only read mouse movement if dragging // started inside the canvas; imitating desktop app behaviour. if (!os->cursor_inside_canvas && !input_mask) - return false; + return; Ref ev; ev.instance(); - dom2godot_mod(p_event, ev); + dom2godot_mod(ev, p_modifiers); ev->set_button_mask(input_mask); - ev->set_position(pos); + ev->set_position(Point2(p_x, p_y)); ev->set_global_position(ev->get_position()); - ev->set_relative(Vector2(p_event->movementX, p_event->movementY)); + ev->set_relative(Vector2(p_rel_x, p_rel_y)); os->input->set_mouse_position(ev->get_position()); ev->set_speed(os->input->get_last_mouse_speed()); os->input->parse_input_event(ev); - // Don't suppress mouseover/-leave events. - return false; } static const char *godot2dom_cursor(OS::CursorShape p_shape) { @@ -579,9 +526,9 @@ OS::MouseMode OS_JavaScript::get_mouse_mode() const { // Wheel -EM_BOOL OS_JavaScript::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); +int OS_JavaScript::mouse_wheel_callback(double p_delta_x, double p_delta_y) { OS_JavaScript *os = get_singleton(); + if (!godot_js_display_canvas_is_focused()) { if (os->cursor_inside_canvas) { godot_js_display_canvas_focus(); @@ -601,16 +548,17 @@ EM_BOOL OS_JavaScript::wheel_callback(int p_event_type, const EmscriptenWheelEve ev->set_control(input->is_key_pressed(KEY_CONTROL)); ev->set_metakey(input->is_key_pressed(KEY_META)); - if (p_event->deltaY < 0) + if (p_delta_y < 0) { ev->set_button_index(BUTTON_WHEEL_UP); - else if (p_event->deltaY > 0) + } else if (p_delta_y > 0) { ev->set_button_index(BUTTON_WHEEL_DOWN); - else if (p_event->deltaX > 0) + } else if (p_delta_x > 0) { ev->set_button_index(BUTTON_WHEEL_LEFT); - else if (p_event->deltaX < 0) + } else if (p_delta_x < 0) { ev->set_button_index(BUTTON_WHEEL_RIGHT); - else + } else { return false; + } // Different browsers give wildly different delta values, and we can't // interpret deltaMode, so use default value for wheel events' factor. @@ -635,53 +583,41 @@ bool OS_JavaScript::has_touchscreen_ui_hint() const { return godot_js_display_touchscreen_is_available(); } -EM_BOOL OS_JavaScript::touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) { +void OS_JavaScript::touch_callback(int p_type, int p_count) { OS_JavaScript *os = get_singleton(); - Ref ev; - 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.instance(); - ev->set_index(touch.identifier); - ev->set_position(compute_position_in_canvas(touch.clientX, touch.clientY)); - os->touches[i] = ev->get_position(); - ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART); - - os->input->parse_input_event(ev); - } - - // Make sure to flush all events so we can call restricted APIs inside the event. - os->input->flush_buffered_events(); - // Resume audio context after input in case autoplay was denied. os->resume_audio(); - return true; -} -EM_BOOL OS_JavaScript::touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) { - OS_JavaScript *os = get_singleton(); - Ref ev; - 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.instance(); - ev->set_index(touch.identifier); - ev->set_position(compute_position_in_canvas(touch.clientX, touch.clientY)); - Point2 &prev = os->touches[i]; - ev->set_relative(ev->get_position() - prev); - prev = ev->get_position(); + const JSTouchEvent &touch_event = os->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 ev; + ev.instance(); + ev->set_index(touch_event.identifier[i]); + ev->set_position(point); - os->input->parse_input_event(ev); + Point2 &prev = os->touches[i]; + ev->set_relative(ev->get_position() - prev); + prev = ev->get_position(); + + os->input->parse_input_event(ev); + } else { + // touchstart/touchend + Ref ev; + ev.instance(); + ev->set_index(touch_event.identifier[i]); + ev->set_position(point); + ev->set_pressed(p_type == 0); + os->touches[i] = point; + + os->input->parse_input_event(ev); + + // Make sure to flush all events so we can call restricted APIs inside the event. + os->input->flush_buffered_events(); + } } - return true; } // Gamepad @@ -886,22 +822,16 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, 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_WINDOW_CALLBACK(blur, blur_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 + godot_js_display_mouse_button_cb(&OS_JavaScript::mouse_button_callback); + godot_js_display_mouse_move_cb(&OS_JavaScript::mouse_move_callback); + godot_js_display_mouse_wheel_cb(&OS_JavaScript::mouse_wheel_callback); + godot_js_display_touch_cb(&OS_JavaScript::touch_callback, touch_event.identifier, touch_event.coords); + godot_js_display_key_cb(&OS_JavaScript::key_callback, key_event.code, key_event.key); // For APIs that are not (sufficiently) exposed, a // library is used below (implemented in library_godot_display.js). godot_js_display_notification_cb(&OS_JavaScript::send_notification_callback, diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index 5295e76a616..dd02cafd2f5 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -41,6 +41,19 @@ class OS_JavaScript : public OS_Unix { private: + struct JSTouchEvent { + uint32_t identifier[32] = { 0 }; + double coords[64] = { 0 }; + }; + JSTouchEvent touch_event; + + struct JSKeyEvent { + char code[32] = { 0 }; + char key[32] = { 0 }; + uint8_t modifiers[4] = { 0 }; + }; + JSKeyEvent key_event; + VideoMode video_mode; bool window_maximized; bool entering_fullscreen; @@ -50,14 +63,13 @@ private: EMSCRIPTEN_WEBGL_CONTEXT_HANDLE webgl_ctx; InputDefault *input; - Ref deferred_key_event; CursorShape cursor_shape; Point2 touches[32]; char canvas_id[256]; bool cursor_inside_canvas; Point2i last_click_pos; - double last_click_ms; + uint64_t last_click_ms; int last_click_button_index; MainLoop *main_loop; @@ -70,21 +82,14 @@ private: bool idb_needs_sync; bool idb_is_syncing; - static Point2 compute_position_in_canvas(int x, int y); static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data); static EM_BOOL blur_callback(int p_event_type, const EmscriptenFocusEvent *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 keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); - static EM_BOOL keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); - - static EM_BOOL mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data); - static EM_BOOL mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data); - - static EM_BOOL wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data); - - static EM_BOOL touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data); - static EM_BOOL touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data); + static int mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers); + static void mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers); + static int mouse_wheel_callback(double p_delta_x, double p_delta_y); + static void key_callback(int p_pressed, int p_repeat, int p_modifiers); + static void touch_callback(int p_type, int p_count); static void gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid); static void input_text_callback(const char *p_text, int p_cursor);