2020-05-14 13:20:05 +02:00
/**************************************************************************/
2022-08-28 20:27:45 +02:00
/* display_server_web.cpp */
2020-05-14 13:20:05 +02:00
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
2022-08-28 20:27:45 +02:00
# include "display_server_web.h"
2020-05-01 14:45:45 +02:00
2023-06-08 14:51:32 +02:00
# include "dom_keys.inc"
# include "godot_js.h"
# include "os_web.h"
2023-05-16 14:18:12 +03:00
# include "core/config/project_settings.h"
2023-07-20 09:42:03 -04:00
# include "core/object/callable_method_pointer.h"
2023-06-08 14:51:32 +02:00
# include "servers/rendering/dummy/rasterizer_dummy.h"
2021-10-25 19:16:40 +02:00
# ifdef GLES3_ENABLED
# include "drivers/gles3/rasterizer_gles3.h"
# endif
2020-05-01 14:45:45 +02:00
# include <emscripten.h>
# include <png.h>
# define DOM_BUTTON_LEFT 0
# define DOM_BUTTON_MIDDLE 1
# define DOM_BUTTON_RIGHT 2
# define DOM_BUTTON_XBUTTON1 3
# define DOM_BUTTON_XBUTTON2 4
2022-08-28 20:27:45 +02:00
DisplayServerWeb * DisplayServerWeb : : get_singleton ( ) {
return static_cast < DisplayServerWeb * > ( DisplayServer : : get_singleton ( ) ) ;
2020-05-01 14:45:45 +02:00
}
// Window (canvas)
2022-08-28 20:27:45 +02:00
bool DisplayServerWeb : : check_size_force_redraw ( ) {
2022-11-08 18:33:24 +08:00
bool size_changed = godot_js_display_size_update ( ) ! = 0 ;
2024-04-27 11:56:39 +02:00
if ( size_changed & & rect_changed_callback . is_valid ( ) ) {
2023-07-20 09:42:03 -04:00
Size2i window_size = window_get_size ( ) ;
Variant size = Rect2i ( Point2i ( ) , window_size ) ; // TODO use window_get_position if implemented.
2023-07-11 16:18:10 +02:00
rect_changed_callback . call ( size ) ;
2023-07-20 09:42:03 -04:00
emscripten_set_canvas_element_size ( canvas_id , window_size . x , window_size . y ) ;
2022-11-08 18:33:24 +08:00
}
return size_changed ;
2020-06-29 18:54:20 +02:00
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : fullscreen_change_callback ( int p_fullscreen ) {
2023-07-20 09:42:03 -04:00
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _fullscreen_change_callback ) . call_deferred ( p_fullscreen ) ;
2023-07-20 09:42:03 -04:00
return ;
}
# endif
_fullscreen_change_callback ( p_fullscreen ) ;
}
void DisplayServerWeb : : _fullscreen_change_callback ( int p_fullscreen ) {
2022-08-28 20:27:45 +02:00
DisplayServerWeb * display = get_singleton ( ) ;
2021-09-12 12:19:33 +02:00
if ( p_fullscreen ) {
display - > window_mode = WINDOW_MODE_FULLSCREEN ;
} else {
display - > window_mode = WINDOW_MODE_WINDOWED ;
2020-05-01 14:45:45 +02:00
}
}
2020-10-23 18:33:20 +02:00
// Drag and drop callback.
2023-07-20 09:42:03 -04:00
void DisplayServerWeb : : drop_files_js_callback ( const char * * p_filev , int p_filec ) {
Vector < String > files ;
for ( int i = 0 ; i < p_filec ; i + + ) {
files . push_back ( String : : utf8 ( p_filev [ i ] ) ) ;
}
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _drop_files_js_callback ) . call_deferred ( files ) ;
2023-07-20 09:42:03 -04:00
return ;
}
# endif
_drop_files_js_callback ( files ) ;
}
void DisplayServerWeb : : _drop_files_js_callback ( const Vector < String > & p_files ) {
2022-08-28 20:27:45 +02:00
DisplayServerWeb * ds = get_singleton ( ) ;
2020-05-08 18:53:33 +02:00
if ( ! ds ) {
ERR_FAIL_MSG ( " Unable to drop files because the DisplayServer is not active " ) ;
}
2024-04-27 11:56:39 +02:00
if ( ! ds - > drop_files_callback . is_valid ( ) ) {
2020-05-08 18:53:33 +02:00
return ;
2020-10-23 18:33:20 +02:00
}
2024-03-01 09:52:21 +02:00
Variant v_files = p_files ;
const Variant * v_args [ 1 ] = { & v_files } ;
Variant ret ;
Callable : : CallError ce ;
ds - > drop_files_callback . callp ( ( const Variant * * ) & v_args , 1 , ret , ce ) ;
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
ERR_PRINT ( vformat ( " Failed to execute drop files callback: %s. " , Variant : : get_callable_error_text ( ds - > drop_files_callback , v_args , 1 , ce ) ) ) ;
}
2020-05-08 18:53:33 +02:00
}
2022-08-28 20:27:45 +02:00
// Web quit request callback.
void DisplayServerWeb : : request_quit_callback ( ) {
2023-07-20 09:42:03 -04:00
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
callable_mp_static ( DisplayServerWeb : : _request_quit_callback ) . call_deferred ( ) ;
return ;
}
# endif
_request_quit_callback ( ) ;
}
void DisplayServerWeb : : _request_quit_callback ( ) {
2022-08-28 20:27:45 +02:00
DisplayServerWeb * ds = get_singleton ( ) ;
2024-04-27 11:56:39 +02:00
if ( ds & & ds - > window_event_callback . is_valid ( ) ) {
2020-10-23 18:33:20 +02:00
Variant event = int ( DisplayServer : : WINDOW_EVENT_CLOSE_REQUEST ) ;
2023-07-11 16:18:10 +02:00
ds - > window_event_callback . call ( event ) ;
2020-10-23 18:33:20 +02:00
}
}
2020-05-01 14:45:45 +02:00
// Keys
2023-02-14 09:05:58 +02:00
void DisplayServerWeb : : dom2godot_mod ( Ref < InputEventWithModifiers > ev , int p_mod , Key p_keycode ) {
if ( p_keycode ! = Key : : SHIFT ) {
ev - > set_shift_pressed ( p_mod & 1 ) ;
}
if ( p_keycode ! = Key : : ALT ) {
ev - > set_alt_pressed ( p_mod & 2 ) ;
}
if ( p_keycode ! = Key : : CTRL ) {
ev - > set_ctrl_pressed ( p_mod & 4 ) ;
}
if ( p_keycode ! = Key : : META ) {
ev - > set_meta_pressed ( p_mod & 8 ) ;
}
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : key_callback ( int p_pressed , int p_repeat , int p_modifiers ) {
DisplayServerWeb * ds = get_singleton ( ) ;
2021-09-10 21:46:22 +02:00
JSKeyEvent & key_event = ds - > key_event ;
2023-07-20 09:42:03 -04:00
const String code = String : : utf8 ( key_event . code ) ;
const String key = String : : utf8 ( key_event . key ) ;
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _key_callback ) . call_deferred ( code , key , p_pressed , p_repeat , p_modifiers ) ;
2023-07-20 09:42:03 -04:00
return ;
}
# endif
_key_callback ( code , key , p_pressed , p_repeat , p_modifiers ) ;
}
void DisplayServerWeb : : _key_callback ( const String & p_key_event_code , const String & p_key_event_key , int p_pressed , int p_repeat , int p_modifiers ) {
2021-09-10 21:46:22 +02:00
// Resume audio context after input in case autoplay was denied.
2022-08-28 20:27:45 +02:00
OS_Web : : get_singleton ( ) - > resume_audio ( ) ;
2021-09-10 21:46:22 +02:00
2023-07-11 11:17:35 +03:00
DisplayServerWeb * ds = get_singleton ( ) ;
if ( ds - > ime_started ) {
return ;
}
2022-12-11 01:21:22 +02:00
char32_t c = 0x00 ;
2023-07-20 09:42:03 -04:00
String unicode = p_key_event_key ;
2022-12-11 01:21:22 +02:00
if ( unicode . length ( ) = = 1 ) {
c = unicode [ 0 ] ;
}
2023-07-20 09:42:03 -04:00
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 ) ;
2023-08-03 15:18:26 +02:00
KeyLocation location = dom_code2godot_key_location ( p_key_event_code . utf8 ( ) . get_data ( ) ) ;
2022-12-11 01:21:22 +02:00
2023-07-11 11:17:35 +03:00
DisplayServerWeb : : KeyEvent ke ;
2020-05-01 14:45:45 +02:00
2023-07-11 11:17:35 +03:00
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 ) ;
2023-08-03 15:18:26 +02:00
ke . location = location ;
2023-07-11 11:17:35 +03:00
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 ;
2021-09-12 19:07:44 +02:00
// Make sure to flush all events so we can call restricted APIs inside the event.
Input : : get_singleton ( ) - > flush_buffered_events ( ) ;
2020-05-01 14:45:45 +02:00
}
// Mouse
2022-08-28 20:27:45 +02:00
int DisplayServerWeb : : mouse_button_callback ( int p_pressed , int p_button , double p_x , double p_y , int p_modifiers ) {
2023-07-20 09:42:03 -04:00
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _mouse_button_callback ) . call_deferred ( p_pressed , p_button , p_x , p_y , p_modifiers ) ;
2023-07-20 09:42:03 -04:00
return true ;
}
# endif
return _mouse_button_callback ( p_pressed , p_button , p_x , p_y , p_modifiers ) ;
}
int DisplayServerWeb : : _mouse_button_callback ( int p_pressed , int p_button , double p_x , double p_y , int p_modifiers ) {
2022-08-28 20:27:45 +02:00
DisplayServerWeb * ds = get_singleton ( ) ;
2020-05-01 14:45:45 +02:00
2021-10-25 19:16:40 +02:00
Point2 pos ( p_x , p_y ) ;
2020-05-01 14:45:45 +02:00
Ref < InputEventMouseButton > ev ;
2021-06-17 16:03:09 -06:00
ev . instantiate ( ) ;
2021-10-25 19:16:40 +02:00
ev - > set_position ( pos ) ;
ev - > set_global_position ( pos ) ;
2021-09-10 21:46:22 +02:00
ev - > set_pressed ( p_pressed ) ;
2023-02-14 09:05:58 +02:00
dom2godot_mod ( ev , p_modifiers , Key : : NONE ) ;
2020-05-01 14:45:45 +02:00
2021-09-10 21:46:22 +02:00
switch ( p_button ) {
2020-05-01 14:45:45 +02:00
case DOM_BUTTON_LEFT :
2021-08-13 16:31:57 -05:00
ev - > set_button_index ( MouseButton : : LEFT ) ;
2020-05-01 14:45:45 +02:00
break ;
case DOM_BUTTON_MIDDLE :
2021-08-13 16:31:57 -05:00
ev - > set_button_index ( MouseButton : : MIDDLE ) ;
2020-05-01 14:45:45 +02:00
break ;
case DOM_BUTTON_RIGHT :
2021-08-13 16:31:57 -05:00
ev - > set_button_index ( MouseButton : : RIGHT ) ;
2020-05-01 14:45:45 +02:00
break ;
case DOM_BUTTON_XBUTTON1 :
2021-08-13 16:31:57 -05:00
ev - > set_button_index ( MouseButton : : MB_XBUTTON1 ) ;
2020-05-01 14:45:45 +02:00
break ;
case DOM_BUTTON_XBUTTON2 :
2021-08-13 16:31:57 -05:00
ev - > set_button_index ( MouseButton : : MB_XBUTTON2 ) ;
2020-05-01 14:45:45 +02:00
break ;
default :
return false ;
}
2021-09-10 21:46:22 +02:00
if ( p_pressed ) {
uint64_t diff = ( OS : : get_singleton ( ) - > get_ticks_usec ( ) / 1000 ) - ds - > last_click_ms ;
2020-05-01 14:45:45 +02:00
2021-09-10 21:46:22 +02:00
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 ) ;
2021-08-13 16:31:57 -05:00
ds - > last_click_button_index = MouseButton : : NONE ;
2021-04-13 04:25:44 -04:00
ev - > set_double_click ( true ) ;
2020-05-01 14:45:45 +02:00
}
} else {
2021-09-10 21:46:22 +02:00
ds - > last_click_button_index = ev - > get_button_index ( ) ;
2020-05-01 14:45:45 +02:00
}
2021-04-13 04:25:44 -04:00
if ( ! ev - > is_double_click ( ) ) {
2021-09-10 21:46:22 +02:00
ds - > last_click_ms + = diff ;
ds - > last_click_pos = ev - > get_position ( ) ;
2020-05-01 14:45:45 +02:00
}
}
2023-01-08 00:55:54 +01:00
BitField < MouseButtonMask > mask = Input : : get_singleton ( ) - > get_mouse_button_mask ( ) ;
MouseButtonMask button_flag = mouse_button_to_mask ( ev - > get_button_index ( ) ) ;
2020-05-01 14:45:45 +02:00
if ( ev - > is_pressed ( ) ) {
2023-01-08 00:55:54 +01:00
mask . set_flag ( button_flag ) ;
} else if ( mask . has_flag ( button_flag ) ) {
mask . clear_flag ( button_flag ) ;
2020-05-01 14:45:45 +02:00
} else {
// Received release event, but press was outside the canvas, so ignore.
return false ;
}
ev - > set_button_mask ( mask ) ;
2021-09-10 21:46:22 +02:00
Input : : get_singleton ( ) - > parse_input_event ( ev ) ;
// Resume audio context after input in case autoplay was denied.
2022-08-28 20:27:45 +02:00
OS_Web : : get_singleton ( ) - > resume_audio ( ) ;
2021-09-12 19:07:44 +02:00
// Make sure to flush all events so we can call restricted APIs inside the event.
Input : : get_singleton ( ) - > flush_buffered_events ( ) ;
2020-05-01 14:45:45 +02:00
// Prevent multi-click text selection and wheel-click scrolling anchor.
// Context menu is prevented through contextmenu event.
return true ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : mouse_move_callback ( double p_x , double p_y , double p_rel_x , double p_rel_y , int p_modifiers ) {
2023-07-20 09:42:03 -04:00
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _mouse_move_callback ) . call_deferred ( p_x , p_y , p_rel_x , p_rel_y , p_modifiers ) ;
2023-07-20 09:42:03 -04:00
return ;
}
# endif
_mouse_move_callback ( p_x , p_y , p_rel_x , p_rel_y , p_modifiers ) ;
}
void DisplayServerWeb : : _mouse_move_callback ( double p_x , double p_y , double p_rel_x , double p_rel_y , int p_modifiers ) {
2023-01-08 00:55:54 +01:00
BitField < MouseButtonMask > input_mask = Input : : get_singleton ( ) - > get_mouse_button_mask ( ) ;
2020-05-01 14:45:45 +02:00
// For motion outside the canvas, only read mouse movement if dragging
2022-11-01 15:29:38 +01:00
// started inside the canvas; imitating desktop app behavior.
2023-01-08 00:55:54 +01:00
if ( ! get_singleton ( ) - > cursor_inside_canvas & & input_mask . is_empty ( ) ) {
2021-09-10 21:46:22 +02:00
return ;
2021-08-13 16:31:57 -05:00
}
2020-05-01 14:45:45 +02:00
2021-10-25 19:16:40 +02:00
Point2 pos ( p_x , p_y ) ;
2020-05-01 14:45:45 +02:00
Ref < InputEventMouseMotion > ev ;
2021-06-17 16:03:09 -06:00
ev . instantiate ( ) ;
2023-02-14 09:05:58 +02:00
dom2godot_mod ( ev , p_modifiers , Key : : NONE ) ;
2020-05-01 14:45:45 +02:00
ev - > set_button_mask ( input_mask ) ;
2021-10-25 19:16:40 +02:00
ev - > set_position ( pos ) ;
ev - > set_global_position ( pos ) ;
2020-05-01 14:45:45 +02:00
2021-09-10 21:46:22 +02:00
ev - > set_relative ( Vector2 ( p_rel_x , p_rel_y ) ) ;
2023-10-04 19:20:01 +02:00
ev - > set_relative_screen_position ( ev - > get_relative ( ) ) ;
2021-12-29 13:22:22 +00:00
ev - > set_velocity ( Input : : get_singleton ( ) - > get_last_mouse_velocity ( ) ) ;
2023-10-04 19:20:01 +02:00
ev - > set_screen_velocity ( ev - > get_velocity ( ) ) ;
2020-05-01 14:45:45 +02:00
2021-09-10 21:46:22 +02:00
Input : : get_singleton ( ) - > parse_input_event ( ev ) ;
2020-05-01 14:45:45 +02:00
}
// Cursor
2022-08-28 20:27:45 +02:00
const char * DisplayServerWeb : : godot2dom_cursor ( DisplayServer : : CursorShape p_shape ) {
2020-05-01 14:45:45 +02:00
switch ( p_shape ) {
case DisplayServer : : CURSOR_ARROW :
2022-07-01 20:50:04 +02:00
return " default " ;
2020-05-01 14:45:45 +02:00
case DisplayServer : : CURSOR_IBEAM :
return " text " ;
case DisplayServer : : CURSOR_POINTING_HAND :
return " pointer " ;
case DisplayServer : : CURSOR_CROSS :
return " crosshair " ;
case DisplayServer : : CURSOR_WAIT :
return " wait " ;
2022-05-14 09:48:46 +08:00
case DisplayServer : : CURSOR_BUSY :
return " progress " ;
2020-05-01 14:45:45 +02:00
case DisplayServer : : CURSOR_DRAG :
return " grab " ;
case DisplayServer : : CURSOR_CAN_DROP :
return " grabbing " ;
case DisplayServer : : CURSOR_FORBIDDEN :
return " no-drop " ;
case DisplayServer : : CURSOR_VSIZE :
return " ns-resize " ;
case DisplayServer : : CURSOR_HSIZE :
return " ew-resize " ;
case DisplayServer : : CURSOR_BDIAGSIZE :
return " nesw-resize " ;
case DisplayServer : : CURSOR_FDIAGSIZE :
return " nwse-resize " ;
case DisplayServer : : CURSOR_MOVE :
return " move " ;
case DisplayServer : : CURSOR_VSPLIT :
return " row-resize " ;
case DisplayServer : : CURSOR_HSPLIT :
return " col-resize " ;
case DisplayServer : : CURSOR_HELP :
return " help " ;
default :
2022-07-01 20:50:04 +02:00
return " default " ;
2020-05-01 14:45:45 +02:00
}
}
2022-08-28 20:27:45 +02:00
bool DisplayServerWeb : : tts_is_speaking ( ) const {
2023-05-16 14:18:12 +03:00
ERR_FAIL_COND_V_MSG ( ! tts , false , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 14:33:37 +02:00
return godot_js_tts_is_speaking ( ) ;
}
2022-08-28 20:27:45 +02:00
bool DisplayServerWeb : : tts_is_paused ( ) const {
2023-05-16 14:18:12 +03:00
ERR_FAIL_COND_V_MSG ( ! tts , false , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 14:33:37 +02:00
return godot_js_tts_is_paused ( ) ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : update_voices_callback ( int p_size , const char * * p_voice ) {
2023-07-20 09:42:03 -04:00
Vector < String > voices ;
2021-11-04 14:33:37 +02:00
for ( int i = 0 ; i < p_size ; i + + ) {
2023-07-20 09:42:03 -04:00
voices . append ( String : : utf8 ( p_voice [ i ] ) ) ;
}
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _update_voices_callback ) . call_deferred ( voices ) ;
2023-07-20 09:42:03 -04:00
return ;
}
# endif
_update_voices_callback ( voices ) ;
}
void DisplayServerWeb : : _update_voices_callback ( const Vector < String > & p_voices ) {
get_singleton ( ) - > voices . clear ( ) ;
for ( int i = 0 ; i < p_voices . size ( ) ; i + + ) {
Vector < String > tokens = p_voices [ i ] . split ( " ; " , true , 2 ) ;
2021-11-04 14:33:37 +02:00
if ( tokens . size ( ) = = 2 ) {
Dictionary voice_d ;
voice_d [ " name " ] = tokens [ 1 ] ;
voice_d [ " id " ] = tokens [ 1 ] ;
voice_d [ " language " ] = tokens [ 0 ] ;
get_singleton ( ) - > voices . push_back ( voice_d ) ;
}
}
}
2022-08-28 20:27:45 +02:00
TypedArray < Dictionary > DisplayServerWeb : : tts_get_voices ( ) const {
2023-05-16 14:18:12 +03:00
ERR_FAIL_COND_V_MSG ( ! tts , TypedArray < Dictionary > ( ) , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 14:33:37 +02:00
godot_js_tts_get_voices ( update_voices_callback ) ;
return voices ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : tts_speak ( const String & p_text , const String & p_voice , int p_volume , float p_pitch , float p_rate , int p_utterance_id , bool p_interrupt ) {
2023-05-16 14:18:12 +03:00
ERR_FAIL_COND_MSG ( ! tts , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 14:33:37 +02:00
if ( p_interrupt ) {
tts_stop ( ) ;
}
if ( p_text . is_empty ( ) ) {
tts_post_utterance_event ( DisplayServer : : TTS_UTTERANCE_CANCELED , p_utterance_id ) ;
return ;
}
CharString string = p_text . utf8 ( ) ;
utterance_ids [ p_utterance_id ] = string ;
2023-07-20 09:42:03 -04:00
godot_js_tts_speak ( string . get_data ( ) , p_voice . utf8 ( ) . get_data ( ) , CLAMP ( p_volume , 0 , 100 ) , CLAMP ( p_pitch , 0.f , 2.f ) , CLAMP ( p_rate , 0.1f , 10.f ) , p_utterance_id , DisplayServerWeb : : js_utterance_callback ) ;
2021-11-04 14:33:37 +02:00
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : tts_pause ( ) {
2023-05-16 14:18:12 +03:00
ERR_FAIL_COND_MSG ( ! tts , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 14:33:37 +02:00
godot_js_tts_pause ( ) ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : tts_resume ( ) {
2023-05-16 14:18:12 +03:00
ERR_FAIL_COND_MSG ( ! tts , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 14:33:37 +02:00
godot_js_tts_resume ( ) ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : tts_stop ( ) {
2023-05-16 14:18:12 +03:00
ERR_FAIL_COND_MSG ( ! tts , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2022-05-13 15:04:37 +02:00
for ( const KeyValue < int , CharString > & E : utterance_ids ) {
tts_post_utterance_event ( DisplayServer : : TTS_UTTERANCE_CANCELED , E . key ) ;
2021-11-04 14:33:37 +02:00
}
utterance_ids . clear ( ) ;
godot_js_tts_stop ( ) ;
}
2023-07-20 09:42:03 -04:00
void DisplayServerWeb : : js_utterance_callback ( int p_event , int p_id , int p_pos ) {
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _js_utterance_callback ) . call_deferred ( p_event , p_id , p_pos ) ;
2023-07-20 09:42:03 -04:00
return ;
}
# endif
_js_utterance_callback ( p_event , p_id , p_pos ) ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : _js_utterance_callback ( int p_event , int p_id , int p_pos ) {
DisplayServerWeb * ds = ( DisplayServerWeb * ) DisplayServer : : get_singleton ( ) ;
2021-11-04 14:33:37 +02:00
if ( ds - > utterance_ids . has ( p_id ) ) {
int pos = 0 ;
if ( ( TTSUtteranceEvent ) p_event = = DisplayServer : : TTS_UTTERANCE_BOUNDARY ) {
// Convert position from UTF-8 to UTF-32.
const CharString & string = ds - > utterance_ids [ p_id ] ;
for ( int i = 0 ; i < MIN ( p_pos , string . length ( ) ) ; i + + ) {
uint8_t c = string [ i ] ;
if ( ( c & 0xe0 ) = = 0xc0 ) {
i + = 1 ;
} else if ( ( c & 0xf0 ) = = 0xe0 ) {
i + = 2 ;
} else if ( ( c & 0xf8 ) = = 0xf0 ) {
i + = 3 ;
}
pos + + ;
}
} else if ( ( TTSUtteranceEvent ) p_event ! = DisplayServer : : TTS_UTTERANCE_STARTED ) {
ds - > utterance_ids . erase ( p_id ) ;
}
ds - > tts_post_utterance_event ( ( TTSUtteranceEvent ) p_event , p_id , pos ) ;
}
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : cursor_set_shape ( CursorShape p_shape ) {
2020-05-01 14:45:45 +02:00
ERR_FAIL_INDEX ( p_shape , CURSOR_MAX ) ;
2020-10-23 18:33:20 +02:00
if ( cursor_shape = = p_shape ) {
return ;
2020-05-01 14:45:45 +02:00
}
cursor_shape = p_shape ;
2020-10-23 18:33:20 +02:00
godot_js_display_cursor_set_shape ( godot2dom_cursor ( cursor_shape ) ) ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
DisplayServer : : CursorShape DisplayServerWeb : : cursor_get_shape ( ) const {
2020-05-01 14:45:45 +02:00
return cursor_shape ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : cursor_set_custom_image ( const Ref < Resource > & p_cursor , CursorShape p_shape , const Vector2 & p_hotspot ) {
2023-02-17 14:17:37 +01:00
ERR_FAIL_INDEX ( p_shape , CURSOR_MAX ) ;
2020-05-01 14:45:45 +02:00
if ( p_cursor . is_valid ( ) ) {
2024-06-02 22:05:21 +02:00
Ref < Image > image = _get_cursor_image_from_resource ( p_cursor , p_hotspot ) ;
2024-02-28 18:38:15 +01:00
ERR_FAIL_COND ( image . is_null ( ) ) ;
Vector2i texture_size = image - > get_size ( ) ;
2020-05-01 14:45:45 +02:00
if ( image - > get_format ( ) ! = Image : : FORMAT_RGBA8 ) {
image - > convert ( Image : : FORMAT_RGBA8 ) ;
}
png_image png_meta ;
memset ( & png_meta , 0 , sizeof png_meta ) ;
png_meta . version = PNG_IMAGE_VERSION ;
png_meta . width = texture_size . width ;
png_meta . height = texture_size . height ;
png_meta . format = PNG_FORMAT_RGBA ;
PackedByteArray png ;
size_t len ;
PackedByteArray data = image - > get_data ( ) ;
ERR_FAIL_COND ( ! png_image_write_get_memory_size ( png_meta , len , 0 , data . ptr ( ) , 0 , nullptr ) ) ;
png . resize ( len ) ;
ERR_FAIL_COND ( ! png_image_write_to_memory ( & png_meta , png . ptrw ( ) , & len , 0 , data . ptr ( ) , 0 , nullptr ) ) ;
2020-10-23 18:33:20 +02:00
godot_js_display_cursor_set_custom_shape ( godot2dom_cursor ( p_shape ) , png . ptr ( ) , len , p_hotspot . x , p_hotspot . y ) ;
2020-05-01 14:45:45 +02:00
2020-10-23 18:33:20 +02:00
} else {
2021-04-29 11:47:24 +02:00
godot_js_display_cursor_set_custom_shape ( godot2dom_cursor ( p_shape ) , nullptr , 0 , 0 , 0 ) ;
2020-05-01 14:45:45 +02:00
}
cursor_set_shape ( cursor_shape ) ;
}
// Mouse mode
2025-01-22 10:44:50 -05:00
void DisplayServerWeb : : _mouse_update_mode ( ) {
MouseMode wanted_mouse_mode = mouse_mode_override_enabled
? mouse_mode_override
: mouse_mode_base ;
ERR_FAIL_COND_MSG ( wanted_mouse_mode = = MOUSE_MODE_CONFINED | | wanted_mouse_mode = = MOUSE_MODE_CONFINED_HIDDEN , " MOUSE_MODE_CONFINED is not supported for the Web platform. " ) ;
if ( wanted_mouse_mode = = mouse_get_mode ( ) ) {
2020-05-01 14:45:45 +02:00
return ;
2021-03-30 18:35:08 -04:00
}
2020-05-01 14:45:45 +02:00
2025-01-22 10:44:50 -05:00
if ( wanted_mouse_mode = = MOUSE_MODE_VISIBLE ) {
2020-10-23 18:33:20 +02:00
godot_js_display_cursor_set_visible ( 1 ) ;
2021-09-12 18:13:54 +02:00
godot_js_display_cursor_lock_set ( 0 ) ;
2020-05-01 14:45:45 +02:00
2025-01-22 10:44:50 -05:00
} else if ( wanted_mouse_mode = = MOUSE_MODE_HIDDEN ) {
2020-10-23 18:33:20 +02:00
godot_js_display_cursor_set_visible ( 0 ) ;
2021-09-12 18:13:54 +02:00
godot_js_display_cursor_lock_set ( 0 ) ;
2020-05-01 14:45:45 +02:00
2025-01-22 10:44:50 -05:00
} else if ( wanted_mouse_mode = = MOUSE_MODE_CAPTURED ) {
2020-10-23 18:33:20 +02:00
godot_js_display_cursor_set_visible ( 1 ) ;
2021-09-12 18:13:54 +02:00
godot_js_display_cursor_lock_set ( 1 ) ;
2020-05-01 14:45:45 +02:00
}
}
2025-01-22 10:44:50 -05:00
void DisplayServerWeb : : mouse_set_mode ( MouseMode p_mode ) {
ERR_FAIL_INDEX ( p_mode , MouseMode : : MOUSE_MODE_MAX ) ;
2025-02-11 11:55:01 -05:00
if ( mouse_mode_override_enabled ) {
mouse_mode_base = p_mode ;
// No need to update, as override is enabled.
return ;
}
if ( p_mode = = mouse_mode_base & & p_mode = = mouse_get_mode ( ) ) {
// No need to update, as it is currently set as the correct mode.
2025-01-22 10:44:50 -05:00
return ;
}
2025-02-11 11:55:01 -05:00
2025-01-22 10:44:50 -05:00
mouse_mode_base = p_mode ;
_mouse_update_mode ( ) ;
}
2022-08-28 20:27:45 +02:00
DisplayServer : : MouseMode DisplayServerWeb : : mouse_get_mode ( ) const {
2020-10-23 18:33:20 +02:00
if ( godot_js_display_cursor_is_hidden ( ) ) {
2020-05-01 14:45:45 +02:00
return MOUSE_MODE_HIDDEN ;
2020-10-23 18:33:20 +02:00
}
2020-05-01 14:45:45 +02:00
2021-09-12 18:13:54 +02:00
if ( godot_js_display_cursor_is_locked ( ) ) {
return MOUSE_MODE_CAPTURED ;
}
return MOUSE_MODE_VISIBLE ;
2020-05-01 14:45:45 +02:00
}
2025-01-22 10:44:50 -05:00
void DisplayServerWeb : : mouse_set_mode_override ( MouseMode p_mode ) {
ERR_FAIL_INDEX ( p_mode , MouseMode : : MOUSE_MODE_MAX ) ;
2025-02-11 11:55:01 -05:00
if ( ! mouse_mode_override_enabled ) {
mouse_mode_override = p_mode ;
// No need to update, as override is not enabled.
return ;
}
if ( p_mode = = mouse_mode_override & & p_mode = = mouse_get_mode ( ) ) {
// No need to update, as it is currently set as the correct mode.
2025-01-22 10:44:50 -05:00
return ;
}
2025-02-11 11:55:01 -05:00
2025-01-22 10:44:50 -05:00
mouse_mode_override = p_mode ;
_mouse_update_mode ( ) ;
}
DisplayServer : : MouseMode DisplayServerWeb : : mouse_get_mode_override ( ) const {
return mouse_mode_override ;
}
void DisplayServerWeb : : mouse_set_mode_override_enabled ( bool p_override_enabled ) {
if ( p_override_enabled = = mouse_mode_override_enabled ) {
return ;
}
mouse_mode_override_enabled = p_override_enabled ;
_mouse_update_mode ( ) ;
}
bool DisplayServerWeb : : mouse_is_mode_override_enabled ( ) const {
return mouse_mode_override_enabled ;
}
2022-08-28 20:27:45 +02:00
Point2i DisplayServerWeb : : mouse_get_position ( ) const {
2021-10-25 19:16:40 +02:00
return Input : : get_singleton ( ) - > get_mouse_position ( ) ;
}
2020-05-01 14:45:45 +02:00
// Wheel
2022-08-28 20:27:45 +02:00
int DisplayServerWeb : : mouse_wheel_callback ( double p_delta_x , double p_delta_y ) {
2023-07-20 09:42:03 -04:00
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _mouse_wheel_callback ) . call_deferred ( p_delta_x , p_delta_y ) ;
2023-07-20 09:42:03 -04:00
return true ;
}
# endif
return _mouse_wheel_callback ( p_delta_x , p_delta_y ) ;
}
int DisplayServerWeb : : _mouse_wheel_callback ( double p_delta_x , double p_delta_y ) {
2023-07-11 11:17:35 +03:00
if ( ! godot_js_display_canvas_is_focused ( ) & & ! godot_js_is_ime_focused ( ) ) {
2021-09-10 21:46:22 +02:00
if ( get_singleton ( ) - > cursor_inside_canvas ) {
godot_js_display_canvas_focus ( ) ;
2020-05-01 14:45:45 +02:00
} else {
return false ;
}
}
Input * input = Input : : get_singleton ( ) ;
Ref < InputEventMouseButton > ev ;
2021-06-17 16:03:09 -06:00
ev . instantiate ( ) ;
2020-05-01 14:45:45 +02:00
ev - > set_position ( input - > get_mouse_position ( ) ) ;
ev - > set_global_position ( ev - > get_position ( ) ) ;
2021-08-13 16:31:57 -05:00
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 ) ) ;
2020-05-01 14:45:45 +02:00
2021-09-10 21:46:22 +02:00
if ( p_delta_y < 0 ) {
2021-08-13 16:31:57 -05:00
ev - > set_button_index ( MouseButton : : WHEEL_UP ) ;
2021-09-10 21:46:22 +02:00
} else if ( p_delta_y > 0 ) {
2021-08-13 16:31:57 -05:00
ev - > set_button_index ( MouseButton : : WHEEL_DOWN ) ;
2021-09-10 21:46:22 +02:00
} else if ( p_delta_x > 0 ) {
2021-08-13 16:31:57 -05:00
ev - > set_button_index ( MouseButton : : WHEEL_LEFT ) ;
2021-09-10 21:46:22 +02:00
} else if ( p_delta_x < 0 ) {
2021-08-13 16:31:57 -05:00
ev - > set_button_index ( MouseButton : : WHEEL_RIGHT ) ;
2021-09-10 21:46:22 +02:00
} else {
2020-05-01 14:45:45 +02:00
return false ;
2021-09-10 21:46:22 +02:00
}
2020-05-01 14:45:45 +02:00
// Different browsers give wildly different delta values, and we can't
// interpret deltaMode, so use default value for wheel events' factor.
2023-01-08 00:55:54 +01:00
MouseButtonMask button_flag = mouse_button_to_mask ( ev - > get_button_index ( ) ) ;
BitField < MouseButtonMask > button_mask = input - > get_mouse_button_mask ( ) ;
button_mask . set_flag ( button_flag ) ;
2020-05-01 14:45:45 +02:00
ev - > set_pressed ( true ) ;
2023-01-08 00:55:54 +01:00
ev - > set_button_mask ( button_mask ) ;
2020-05-01 14:45:45 +02:00
input - > parse_input_event ( ev ) ;
2021-09-15 17:13:53 +02:00
Ref < InputEventMouseButton > release = ev - > duplicate ( ) ;
release - > set_pressed ( false ) ;
2023-01-08 00:55:54 +01:00
release - > set_button_mask ( input - > get_mouse_button_mask ( ) ) ;
2021-09-15 17:13:53 +02:00
input - > parse_input_event ( release ) ;
2020-05-01 14:45:45 +02:00
return true ;
}
// Touch
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : touch_callback ( int p_type , int p_count ) {
2023-07-20 09:42:03 -04:00
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _touch_callback ) . call_deferred ( p_type , p_count ) ;
2023-07-20 09:42:03 -04:00
return ;
}
# endif
_touch_callback ( p_type , p_count ) ;
}
void DisplayServerWeb : : _touch_callback ( int p_type , int p_count ) {
2022-08-28 20:27:45 +02:00
DisplayServerWeb * ds = get_singleton ( ) ;
2020-05-01 14:45:45 +02:00
2021-09-10 21:46:22 +02:00
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 ) ;
2023-10-04 19:20:01 +02:00
ev - > set_relative_screen_position ( ev - > get_relative ( ) ) ;
2021-09-10 21:46:22 +02:00
prev = ev - > get_position ( ) ;
Input : : get_singleton ( ) - > parse_input_event ( ev ) ;
} else {
// touchstart/touchend
Ref < InputEventScreenTouch > ev ;
2021-09-12 19:07:44 +02:00
2021-09-10 21:46:22 +02:00
// Resume audio context after input in case autoplay was denied.
2022-08-28 20:27:45 +02:00
OS_Web : : get_singleton ( ) - > resume_audio ( ) ;
2021-09-12 19:07:44 +02:00
2021-09-10 21:46:22 +02:00
ev . instantiate ( ) ;
ev - > set_index ( touch_event . identifier [ i ] ) ;
ev - > set_position ( point ) ;
ev - > set_pressed ( p_type = = 0 ) ;
ds - > touches [ i ] = point ;
2020-05-01 14:45:45 +02:00
2021-09-10 21:46:22 +02:00
Input : : get_singleton ( ) - > parse_input_event ( ev ) ;
2020-05-01 14:45:45 +02:00
2021-09-10 21:46:22 +02:00
// Make sure to flush all events so we can call restricted APIs inside the event.
Input : : get_singleton ( ) - > flush_buffered_events ( ) ;
}
2020-05-01 14:45:45 +02:00
}
}
2022-10-17 00:59:51 +02:00
bool DisplayServerWeb : : is_touchscreen_available ( ) const {
return godot_js_display_touchscreen_is_available ( ) | | ( Input : : get_singleton ( ) & & Input : : get_singleton ( ) - > is_emulating_touch_from_mouse ( ) ) ;
2020-05-01 14:45:45 +02:00
}
2021-05-20 12:07:26 +02:00
// Virtual Keyboard
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : vk_input_text_callback ( const char * p_text , int p_cursor ) {
2023-07-11 11:17:35 +03:00
String text = String : : utf8 ( p_text ) ;
2023-07-20 09:42:03 -04:00
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _vk_input_text_callback ) . call_deferred ( text , p_cursor ) ;
2023-07-20 09:42:03 -04:00
return ;
}
# endif
_vk_input_text_callback ( text , p_cursor ) ;
}
void DisplayServerWeb : : _vk_input_text_callback ( const String & p_text , int p_cursor ) {
2022-08-28 20:27:45 +02:00
DisplayServerWeb * ds = DisplayServerWeb : : get_singleton ( ) ;
2024-04-27 11:56:39 +02:00
if ( ! ds | | ! ds - > input_text_callback . is_valid ( ) ) {
2021-03-08 23:16:51 +01:00
return ;
}
// Call input_text
2023-07-20 09:42:03 -04:00
ds - > input_text_callback . call ( p_text ) ;
2021-03-08 23:16:51 +01:00
// Insert key right to reach position.
Input * input = Input : : get_singleton ( ) ;
Ref < InputEventKey > k ;
for ( int i = 0 ; i < p_cursor ; i + + ) {
2021-06-17 16:03:09 -06:00
k . instantiate ( ) ;
2021-03-08 23:16:51 +01:00
k - > set_pressed ( true ) ;
k - > set_echo ( false ) ;
2021-08-13 16:31:57 -05:00
k - > set_keycode ( Key : : RIGHT ) ;
2021-03-08 23:16:51 +01:00
input - > parse_input_event ( k ) ;
2021-06-17 16:03:09 -06:00
k . instantiate ( ) ;
2021-03-08 23:16:51 +01:00
k - > set_pressed ( false ) ;
k - > set_echo ( false ) ;
2021-08-13 16:31:57 -05:00
k - > set_keycode ( Key : : RIGHT ) ;
2021-03-08 23:16:51 +01:00
input - > parse_input_event ( k ) ;
}
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : virtual_keyboard_show ( const String & p_existing_text , const Rect2 & p_screen_rect , VirtualKeyboardType p_type , int p_max_input_length , int p_cursor_start , int p_cursor_end ) {
2022-07-07 14:20:10 -04:00
godot_js_display_vk_show ( p_existing_text . utf8 ( ) . get_data ( ) , p_type , p_cursor_start , p_cursor_end ) ;
2021-03-08 23:16:51 +01:00
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : virtual_keyboard_hide ( ) {
2021-03-08 23:16:51 +01:00
godot_js_display_vk_hide ( ) ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_blur_callback ( ) {
2023-07-20 09:42:03 -04:00
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
callable_mp_static ( DisplayServerWeb : : _window_blur_callback ) . call_deferred ( ) ;
return ;
}
# endif
_window_blur_callback ( ) ;
}
void DisplayServerWeb : : _window_blur_callback ( ) {
2021-09-18 09:04:09 +03:00
Input : : get_singleton ( ) - > release_pressed_events ( ) ;
}
2020-05-01 14:45:45 +02:00
// Gamepad
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : gamepad_callback ( int p_index , int p_connected , const char * p_id , const char * p_guid ) {
2023-07-20 09:42:03 -04:00
String id = p_id ;
String guid = p_guid ;
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _gamepad_callback ) . call_deferred ( p_index , p_connected , id , guid ) ;
2023-07-20 09:42:03 -04:00
return ;
}
# endif
_gamepad_callback ( p_index , p_connected , id , guid ) ;
}
void DisplayServerWeb : : _gamepad_callback ( int p_index , int p_connected , const String & p_id , const String & p_guid ) {
2020-05-01 14:45:45 +02:00
Input * input = Input : : get_singleton ( ) ;
2020-12-27 14:15:43 +01:00
if ( p_connected ) {
2023-07-20 09:42:03 -04:00
input - > joy_connection_changed ( p_index , true , p_id , p_guid ) ;
2020-05-01 14:45:45 +02:00
} else {
2020-12-27 14:15:43 +01:00
input - > joy_connection_changed ( p_index , false , " " ) ;
2020-05-01 14:45:45 +02:00
}
}
2023-07-11 11:17:35 +03:00
// 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 ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _ime_callback ) . call_deferred ( p_type , text ) ;
2023-07-11 11:17:35 +03:00
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 ;
}
2024-05-08 10:28:55 +03:00
ds - > process_keys ( ) ;
Input : : get_singleton ( ) - > flush_buffered_events ( ) ;
2023-07-11 11:17:35 +03:00
}
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 ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : process_joypads ( ) {
2020-05-01 14:45:45 +02:00
Input * input = Input : : get_singleton ( ) ;
2021-09-12 13:01:15 +02:00
int32_t pads = godot_js_input_gamepad_sample_count ( ) ;
2020-12-27 14:15:43 +01:00
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 + + ) {
2021-09-12 13:01:15 +02:00
int err = godot_js_input_gamepad_sample_get ( idx , s_btns , & s_btns_num , s_axes , & s_axes_num , & s_standard ) ;
2020-12-27 14:15:43 +01:00
if ( err ) {
continue ;
}
for ( int b = 0 ; b < s_btns_num ; b + + ) {
// Buttons 6 and 7 in the standard mapping need to be
2021-08-13 16:31:57 -05:00
// axis to be handled as JoyAxis::TRIGGER by Godot.
2024-07-25 00:35:05 -03:00
if ( s_standard & & ( b = = 6 ) ) {
input - > joy_axis ( idx , JoyAxis : : TRIGGER_LEFT , s_btns [ b ] ) ;
} else if ( s_standard & & ( b = = 7 ) ) {
input - > joy_axis ( idx , JoyAxis : : TRIGGER_RIGHT , s_btns [ b ] ) ;
2020-12-27 14:15:43 +01:00
} else {
2021-12-25 09:29:08 +00:00
input - > joy_button ( idx , ( JoyButton ) b , s_btns [ b ] ) ;
2020-05-01 14:45:45 +02:00
}
}
2020-12-27 14:15:43 +01:00
for ( int a = 0 ; a < s_axes_num ; a + + ) {
2021-12-25 09:29:08 +00:00
input - > joy_axis ( idx , ( JoyAxis ) a , s_axes [ a ] ) ;
2020-12-27 14:15:43 +01:00
}
2020-05-01 14:45:45 +02:00
}
}
2022-08-28 20:27:45 +02:00
Vector < String > DisplayServerWeb : : get_rendering_drivers_func ( ) {
2020-05-01 14:45:45 +02:00
Vector < String > drivers ;
2021-10-25 19:16:40 +02:00
# ifdef GLES3_ENABLED
drivers . push_back ( " opengl3 " ) ;
# endif
2020-05-01 14:45:45 +02:00
return drivers ;
}
// Clipboard
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : update_clipboard_callback ( const char * p_text ) {
2023-07-20 09:42:03 -04:00
String text = p_text ;
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _update_clipboard_callback ) . call_deferred ( text ) ;
2023-07-20 09:42:03 -04:00
return ;
}
# endif
_update_clipboard_callback ( text ) ;
}
void DisplayServerWeb : : _update_clipboard_callback ( const String & p_text ) {
get_singleton ( ) - > clipboard = p_text ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : clipboard_set ( const String & p_text ) {
2020-10-23 18:33:20 +02:00
clipboard = p_text ;
int err = godot_js_display_clipboard_set ( p_text . utf8 ( ) . get_data ( ) ) ;
2020-05-01 14:45:45 +02:00
ERR_FAIL_COND_MSG ( err , " Clipboard API is not supported. " ) ;
}
2022-08-28 20:27:45 +02:00
String DisplayServerWeb : : clipboard_get ( ) const {
2020-10-23 18:33:20 +02:00
godot_js_display_clipboard_get ( update_clipboard_callback ) ;
2020-05-01 14:45:45 +02:00
return clipboard ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : send_window_event_callback ( int p_notification ) {
2023-07-20 09:42:03 -04:00
# ifdef PROXY_TO_PTHREAD_ENABLED
if ( ! Thread : : is_main_thread ( ) ) {
2024-05-27 13:29:57 +02:00
callable_mp_static ( DisplayServerWeb : : _send_window_event_callback ) . call_deferred ( p_notification ) ;
2023-07-20 09:42:03 -04:00
return ;
}
# endif
_send_window_event_callback ( p_notification ) ;
}
void DisplayServerWeb : : _send_window_event_callback ( int p_notification ) {
2022-08-28 20:27:45 +02:00
DisplayServerWeb * ds = get_singleton ( ) ;
2020-10-23 18:33:20 +02:00
if ( ! ds ) {
return ;
}
2020-05-01 14:45:45 +02:00
if ( p_notification = = DisplayServer : : WINDOW_EVENT_MOUSE_ENTER | | p_notification = = DisplayServer : : WINDOW_EVENT_MOUSE_EXIT ) {
2020-10-23 18:33:20 +02:00
ds - > cursor_inside_canvas = p_notification = = DisplayServer : : WINDOW_EVENT_MOUSE_ENTER ;
2020-05-01 14:45:45 +02:00
}
2023-07-11 11:17:35 +03:00
if ( godot_js_is_ime_focused ( ) & & ( p_notification = = DisplayServer : : WINDOW_EVENT_FOCUS_IN | | p_notification = = DisplayServer : : WINDOW_EVENT_FOCUS_OUT ) ) {
return ;
}
2024-04-27 11:56:39 +02:00
if ( ds - > window_event_callback . is_valid ( ) ) {
2020-05-01 14:45:45 +02:00
Variant event = int ( p_notification ) ;
2023-07-11 16:18:10 +02:00
ds - > window_event_callback . call ( event ) ;
2020-05-01 14:45:45 +02:00
}
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : set_icon ( const Ref < Image > & p_icon ) {
2023-06-19 16:18:39 +03:00
if ( p_icon . is_valid ( ) ) {
ERR_FAIL_COND ( p_icon - > get_width ( ) < = 0 | | p_icon - > get_height ( ) < = 0 ) ;
Ref < Image > icon = p_icon ;
if ( icon - > is_compressed ( ) ) {
2020-05-01 14:45:45 +02:00
icon = icon - > duplicate ( ) ;
2023-06-19 16:18:39 +03:00
ERR_FAIL_COND ( icon - > decompress ( ) ! = OK ) ;
}
if ( icon - > get_format ( ) ! = Image : : FORMAT_RGBA8 ) {
if ( icon = = p_icon ) {
icon = icon - > duplicate ( ) ;
}
icon - > convert ( Image : : FORMAT_RGBA8 ) ;
2022-02-16 13:56:32 +01:00
}
2020-05-01 14:45:45 +02:00
2023-06-19 16:18:39 +03:00
png_image png_meta ;
memset ( & png_meta , 0 , sizeof png_meta ) ;
png_meta . version = PNG_IMAGE_VERSION ;
png_meta . width = icon - > get_width ( ) ;
png_meta . height = icon - > get_height ( ) ;
png_meta . format = PNG_FORMAT_RGBA ;
2020-05-01 14:45:45 +02:00
2023-06-19 16:18:39 +03:00
PackedByteArray png ;
size_t len ;
PackedByteArray data = icon - > get_data ( ) ;
ERR_FAIL_COND ( ! png_image_write_get_memory_size ( png_meta , len , 0 , data . ptr ( ) , 0 , nullptr ) ) ;
2020-05-01 14:45:45 +02:00
2023-06-19 16:18:39 +03:00
png . resize ( len ) ;
ERR_FAIL_COND ( ! png_image_write_to_memory ( & png_meta , png . ptrw ( ) , & len , 0 , data . ptr ( ) , 0 , nullptr ) ) ;
2020-05-01 14:45:45 +02:00
2023-06-19 16:18:39 +03:00
godot_js_display_window_icon_set ( png . ptr ( ) , len ) ;
} else {
godot_js_display_window_icon_set ( nullptr , 0 ) ;
}
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : _dispatch_input_event ( const Ref < InputEvent > & p_event ) {
2020-05-01 14:45:45 +02:00
Callable cb = get_singleton ( ) - > input_event_callback ;
2023-07-11 16:18:10 +02:00
if ( cb . is_valid ( ) ) {
cb . call ( p_event ) ;
2020-05-01 14:45:45 +02:00
}
}
2024-09-25 16:57:23 -04:00
DisplayServer * DisplayServerWeb : : create_func ( const String & p_rendering_driver , WindowMode p_window_mode , VSyncMode p_vsync_mode , uint32_t p_flags , const Point2i * p_position , const Size2i & p_resolution , int p_screen , Context p_context , int64_t p_parent_window , Error & r_error ) {
return memnew ( DisplayServerWeb ( p_rendering_driver , p_window_mode , p_vsync_mode , p_flags , p_position , p_resolution , p_screen , p_context , p_parent_window , r_error ) ) ;
2020-05-01 14:45:45 +02:00
}
2024-09-25 16:57:23 -04:00
DisplayServerWeb : : DisplayServerWeb ( const String & p_rendering_driver , WindowMode p_window_mode , VSyncMode p_vsync_mode , uint32_t p_flags , const Point2i * p_position , const Size2i & p_resolution , int p_screen , Context p_context , int64_t p_parent_window , Error & r_error ) {
2020-07-27 13:46:38 +02:00
r_error = OK ; // Always succeeds for now.
2023-05-16 14:18:12 +03:00
tts = GLOBAL_GET ( " audio/general/text_to_speech " ) ;
2024-01-19 19:41:01 +02:00
native_menu = memnew ( NativeMenu ) ; // Dummy native menu.
2023-05-16 14:18:12 +03:00
2020-10-23 18:33:20 +02:00
// Ensure the canvas ID.
godot_js_config_canvas_id_get ( canvas_id , 256 ) ;
2021-02-10 16:22:48 +01:00
// Handle contextmenu, webglcontextlost
2022-01-28 11:19:53 +02:00
godot_js_display_setup_canvas ( p_resolution . x , p_resolution . y , ( p_window_mode = = WINDOW_MODE_FULLSCREEN | | p_window_mode = = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) , OS : : get_singleton ( ) - > is_hidpi_allowed ( ) ? 1 : 0 ) ;
2021-02-10 16:22:48 +01:00
2020-10-23 18:33:20 +02:00
// Check if it's windows.
swap_cancel_ok = godot_js_display_is_swap_ok_cancel ( ) = = 1 ;
// Expose method for requesting quit.
godot_js_os_request_quit_cb ( request_quit_callback ) ;
2020-07-27 13:53:03 +02:00
2021-10-25 19:16:40 +02:00
# ifdef GLES3_ENABLED
2022-11-05 13:55:45 +01:00
bool webgl2_inited = false ;
if ( godot_js_display_has_webgl ( 2 ) ) {
2021-10-25 19:16:40 +02:00
EmscriptenWebGLContextAttributes attributes ;
emscripten_webgl_init_context_attributes ( & attributes ) ;
2022-09-08 09:44:14 +02:00
attributes . alpha = OS : : get_singleton ( ) - > is_layered_allowed ( ) ;
2021-10-25 19:16:40 +02:00
attributes . antialias = false ;
attributes . majorVersion = 2 ;
2022-09-08 09:44:14 +02:00
attributes . explicitSwapControl = true ;
2021-10-25 19:16:40 +02:00
webgl_ctx = emscripten_webgl_create_context ( canvas_id , & attributes ) ;
2022-11-05 13:55:45 +01:00
webgl2_inited = webgl_ctx & & emscripten_webgl_make_context_current ( webgl_ctx ) = = EMSCRIPTEN_RESULT_SUCCESS ;
2020-05-01 14:45:45 +02:00
}
2022-11-05 13:55:45 +01:00
if ( webgl2_inited ) {
if ( ! emscripten_webgl_enable_extension ( webgl_ctx , " OVR_multiview2 " ) ) {
print_verbose ( " Failed to enable WebXR extension. " ) ;
}
2021-11-12 14:49:49 +02:00
RasterizerGLES3 : : make_current ( false ) ;
2022-11-05 13:55:45 +01:00
} else {
2023-01-17 15:26:10 +01:00
OS : : get_singleton ( ) - > alert (
" Your browser seems not to support WebGL 2. \n \n "
" If possible, consider updating your browser version and video card drivers. " ,
" Unable to initialize WebGL 2 video driver " ) ;
2021-10-25 19:16:40 +02:00
RasterizerDummy : : make_current ( ) ;
}
# else
RasterizerDummy : : make_current ( ) ;
2020-05-01 14:45:45 +02:00
# endif
2021-09-12 13:01:15 +02:00
// JS Input interface (js/libs/library_godot_input.js)
2022-08-28 20:27:45 +02:00
godot_js_input_mouse_button_cb ( & DisplayServerWeb : : mouse_button_callback ) ;
godot_js_input_mouse_move_cb ( & DisplayServerWeb : : mouse_move_callback ) ;
godot_js_input_mouse_wheel_cb ( & DisplayServerWeb : : mouse_wheel_callback ) ;
godot_js_input_touch_cb ( & DisplayServerWeb : : touch_callback , touch_event . identifier , touch_event . coords ) ;
godot_js_input_key_cb ( & DisplayServerWeb : : key_callback , key_event . code , key_event . key ) ;
2023-07-20 09:42:03 -04:00
godot_js_input_paste_cb ( & DisplayServerWeb : : update_clipboard_callback ) ;
godot_js_input_drop_files_cb ( & DisplayServerWeb : : drop_files_js_callback ) ;
2022-08-28 20:27:45 +02:00
godot_js_input_gamepad_cb ( & DisplayServerWeb : : gamepad_callback ) ;
2023-07-11 11:17:35 +03:00
godot_js_set_ime_cb ( & DisplayServerWeb : : ime_callback , & DisplayServerWeb : : key_callback , key_event . code , key_event . key ) ;
2021-09-12 13:01:15 +02:00
// JS Display interface (js/libs/library_godot_display.js)
2022-08-28 20:27:45 +02:00
godot_js_display_fullscreen_cb ( & DisplayServerWeb : : fullscreen_change_callback ) ;
2023-07-20 09:42:03 -04:00
godot_js_display_window_blur_cb ( & DisplayServerWeb : : window_blur_callback ) ;
godot_js_display_notification_cb ( & DisplayServerWeb : : send_window_event_callback ,
2020-10-23 18:33:20 +02:00
WINDOW_EVENT_MOUSE_ENTER ,
WINDOW_EVENT_MOUSE_EXIT ,
WINDOW_EVENT_FOCUS_IN ,
WINDOW_EVENT_FOCUS_OUT ) ;
2023-07-20 09:42:03 -04:00
godot_js_display_vk_cb ( & DisplayServerWeb : : vk_input_text_callback ) ;
2020-05-01 14:45:45 +02:00
Input : : get_singleton ( ) - > set_event_dispatch_function ( _dispatch_input_event ) ;
}
2022-08-28 20:27:45 +02:00
DisplayServerWeb : : ~ DisplayServerWeb ( ) {
2024-01-19 19:41:01 +02:00
if ( native_menu ) {
memdelete ( native_menu ) ;
native_menu = nullptr ;
}
2021-10-25 19:16:40 +02:00
# ifdef GLES3_ENABLED
if ( webgl_ctx ) {
emscripten_webgl_commit_frame ( ) ;
emscripten_webgl_destroy_context ( webgl_ctx ) ;
}
# endif
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
bool DisplayServerWeb : : has_feature ( Feature p_feature ) const {
2020-05-01 14:45:45 +02:00
switch ( p_feature ) {
2024-01-19 19:41:01 +02:00
# ifndef DISABLE_DEPRECATED
case FEATURE_GLOBAL_MENU : {
return ( native_menu & & native_menu - > has_feature ( NativeMenu : : FEATURE_GLOBAL_MENU ) ) ;
} break ;
# endif
2020-05-01 14:45:45 +02:00
//case FEATURE_HIDPI:
case FEATURE_ICON :
case FEATURE_CLIPBOARD :
case FEATURE_CURSOR_SHAPE :
case FEATURE_CUSTOM_CURSOR_SHAPE :
case FEATURE_MOUSE :
case FEATURE_TOUCHSCREEN :
return true ;
//case FEATURE_MOUSE_WARP:
//case FEATURE_NATIVE_DIALOG:
2024-03-26 15:18:06 +02:00
//case FEATURE_NATIVE_DIALOG_INPUT:
//case FEATURE_NATIVE_DIALOG_FILE:
2024-10-21 14:13:44 +05:30
//case FEATURE_NATIVE_DIALOG_FILE_EXTRA:
2024-11-17 12:25:23 +02:00
//case FEATURE_NATIVE_DIALOG_FILE_MIME:
2020-05-01 14:45:45 +02:00
//case FEATURE_NATIVE_ICON:
//case FEATURE_WINDOW_TRANSPARENCY:
//case FEATURE_KEEP_SCREEN_ON:
//case FEATURE_ORIENTATION:
2023-07-11 11:17:35 +03:00
case FEATURE_IME :
// IME does not work with experimental VK support.
return godot_js_display_vk_available ( ) = = 0 ;
2021-03-08 23:16:51 +01:00
case FEATURE_VIRTUAL_KEYBOARD :
return godot_js_display_vk_available ( ) ! = 0 ;
2021-11-04 14:33:37 +02:00
case FEATURE_TEXT_TO_SPEECH :
2023-05-16 14:18:12 +03:00
return tts & & ( godot_js_display_tts_available ( ) ! = 0 ) ;
2020-05-01 14:45:45 +02:00
default :
return false ;
}
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : register_web_driver ( ) {
register_create_function ( " web " , create_func , get_rendering_drivers_func ) ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
String DisplayServerWeb : : get_name ( ) const {
return " web " ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
int DisplayServerWeb : : get_screen_count ( ) const {
2020-05-01 14:45:45 +02:00
return 1 ;
}
2023-01-05 00:00:02 +02:00
int DisplayServerWeb : : get_primary_screen ( ) const {
return 0 ;
}
2022-08-28 20:27:45 +02:00
Point2i DisplayServerWeb : : screen_get_position ( int p_screen ) const {
2020-05-01 14:45:45 +02:00
return Point2i ( ) ; // TODO offsetX/Y?
}
2022-08-28 20:27:45 +02:00
Size2i DisplayServerWeb : : screen_get_size ( int p_screen ) const {
2021-01-30 11:35:03 +01:00
int size [ 2 ] ;
godot_js_display_screen_size_get ( size , size + 1 ) ;
return Size2 ( size [ 0 ] , size [ 1 ] ) ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
Rect2i DisplayServerWeb : : screen_get_usable_rect ( int p_screen ) const {
2021-01-30 11:35:03 +01:00
int size [ 2 ] ;
godot_js_display_window_size_get ( size , size + 1 ) ;
return Rect2i ( 0 , 0 , size [ 0 ] , size [ 1 ] ) ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
int DisplayServerWeb : : screen_get_dpi ( int p_screen ) const {
2021-02-12 10:50:02 +01:00
return godot_js_display_screen_dpi_get ( ) ;
}
2022-08-28 20:27:45 +02:00
float DisplayServerWeb : : screen_get_scale ( int p_screen ) const {
2021-02-12 10:50:02 +01:00
return godot_js_display_pixel_ratio_get ( ) ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
float DisplayServerWeb : : screen_get_refresh_rate ( int p_screen ) const {
return SCREEN_REFRESH_RATE_FALLBACK ; // Web doesn't have much of a need for the screen refresh rate, and there's no native way to do so.
2022-01-27 13:46:57 -06:00
}
2022-08-28 20:27:45 +02:00
Vector < DisplayServer : : WindowID > DisplayServerWeb : : get_window_list ( ) const {
2020-05-01 14:45:45 +02:00
Vector < WindowID > ret ;
ret . push_back ( MAIN_WINDOW_ID ) ;
return ret ;
}
2022-08-28 20:27:45 +02:00
DisplayServerWeb : : WindowID DisplayServerWeb : : get_window_at_screen_position ( const Point2i & p_position ) const {
2020-05-01 14:45:45 +02:00
return MAIN_WINDOW_ID ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_attach_instance_id ( ObjectID p_instance , WindowID p_window ) {
2020-05-01 14:45:45 +02:00
window_attached_instance_id = p_instance ;
}
2022-08-28 20:27:45 +02:00
ObjectID DisplayServerWeb : : window_get_attached_instance_id ( WindowID p_window ) const {
2020-05-01 14:45:45 +02:00
return window_attached_instance_id ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_rect_changed_callback ( const Callable & p_callable , WindowID p_window ) {
2022-11-08 18:33:24 +08:00
rect_changed_callback = p_callable ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_window_event_callback ( const Callable & p_callable , WindowID p_window ) {
2020-05-01 14:45:45 +02:00
window_event_callback = p_callable ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_input_event_callback ( const Callable & p_callable , WindowID p_window ) {
2020-05-01 14:45:45 +02:00
input_event_callback = p_callable ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_input_text_callback ( const Callable & p_callable , WindowID p_window ) {
2021-03-08 23:16:51 +01:00
input_text_callback = p_callable ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_drop_files_callback ( const Callable & p_callable , WindowID p_window ) {
2020-05-08 18:53:33 +02:00
drop_files_callback = p_callable ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_title ( const String & p_title , WindowID p_window ) {
2020-10-23 18:33:20 +02:00
godot_js_display_window_title_set ( p_title . utf8 ( ) . get_data ( ) ) ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
int DisplayServerWeb : : window_get_current_screen ( WindowID p_window ) const {
2020-05-01 14:45:45 +02:00
return 1 ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_current_screen ( int p_screen , WindowID p_window ) {
2020-05-01 14:45:45 +02:00
// Not implemented.
}
2022-08-28 20:27:45 +02:00
Point2i DisplayServerWeb : : window_get_position ( WindowID p_window ) const {
2022-11-30 10:28:16 +02:00
return Point2i ( ) ;
}
Point2i DisplayServerWeb : : window_get_position_with_decorations ( WindowID p_window ) const {
return Point2i ( ) ;
2020-05-01 14:45:45 +02:00
}
2020-05-14 14:29:06 +02:00
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_position ( const Point2i & p_position , WindowID p_window ) {
2020-05-01 14:45:45 +02:00
// Not supported.
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_transient ( WindowID p_window , WindowID p_parent ) {
2020-05-01 14:45:45 +02:00
// Not supported.
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_max_size ( const Size2i p_size , WindowID p_window ) {
2020-05-01 14:45:45 +02:00
// Not supported.
}
2022-08-28 20:27:45 +02:00
Size2i DisplayServerWeb : : window_get_max_size ( WindowID p_window ) const {
2020-05-01 14:45:45 +02:00
return Size2i ( ) ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_min_size ( const Size2i p_size , WindowID p_window ) {
2020-05-01 14:45:45 +02:00
// Not supported.
}
2022-08-28 20:27:45 +02:00
Size2i DisplayServerWeb : : window_get_min_size ( WindowID p_window ) const {
2020-05-01 14:45:45 +02:00
return Size2i ( ) ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_size ( const Size2i p_size , WindowID p_window ) {
2021-01-30 11:35:03 +01:00
godot_js_display_desired_size_set ( p_size . x , p_size . y ) ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
Size2i DisplayServerWeb : : window_get_size ( WindowID p_window ) const {
2021-01-30 11:35:03 +01:00
int size [ 2 ] ;
godot_js_display_window_size_get ( size , size + 1 ) ;
return Size2i ( size [ 0 ] , size [ 1 ] ) ;
2020-05-01 14:45:45 +02:00
}
2022-11-30 10:28:16 +02:00
Size2i DisplayServerWeb : : window_get_size_with_decorations ( WindowID p_window ) const {
2020-05-01 14:45:45 +02:00
return window_get_size ( p_window ) ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_mode ( WindowMode p_mode , WindowID p_window ) {
2022-02-16 13:56:32 +01:00
if ( window_mode = = p_mode ) {
2020-05-01 14:45:45 +02:00
return ;
2022-02-16 13:56:32 +01:00
}
2020-05-01 14:45:45 +02:00
switch ( p_mode ) {
case WINDOW_MODE_WINDOWED : {
if ( window_mode = = WINDOW_MODE_FULLSCREEN ) {
2021-01-30 11:35:03 +01:00
godot_js_display_fullscreen_exit ( ) ;
2020-05-01 14:45:45 +02:00
}
window_mode = WINDOW_MODE_WINDOWED ;
} break ;
2022-01-28 11:19:53 +02:00
case WINDOW_MODE_EXCLUSIVE_FULLSCREEN :
2020-05-01 14:45:45 +02:00
case WINDOW_MODE_FULLSCREEN : {
2021-01-30 11:35:03 +01:00
int result = godot_js_display_fullscreen_request ( ) ;
2022-08-28 20:27:45 +02:00
ERR_FAIL_COND_MSG ( result , " The request was denied. Remember that enabling fullscreen is only possible from an input callback for the Web platform. " ) ;
2020-05-01 14:45:45 +02:00
} break ;
case WINDOW_MODE_MAXIMIZED :
case WINDOW_MODE_MINIMIZED :
2022-09-12 16:29:40 +02:00
// WindowMode MAXIMIZED and MINIMIZED are not supported in Web platform.
2020-05-01 14:45:45 +02:00
break ;
default :
break ;
}
}
2022-08-28 20:27:45 +02:00
DisplayServerWeb : : WindowMode DisplayServerWeb : : window_get_mode ( WindowID p_window ) const {
2020-05-01 14:45:45 +02:00
return window_mode ;
}
2022-08-28 20:27:45 +02:00
bool DisplayServerWeb : : window_is_maximize_allowed ( WindowID p_window ) const {
2020-05-01 14:45:45 +02:00
return false ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_set_flag ( WindowFlags p_flag , bool p_enabled , WindowID p_window ) {
2020-05-01 14:45:45 +02:00
// Not supported.
}
2022-08-28 20:27:45 +02:00
bool DisplayServerWeb : : window_get_flag ( WindowFlags p_flag , WindowID p_window ) const {
2020-05-01 14:45:45 +02:00
return false ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_request_attention ( WindowID p_window ) {
2020-05-01 14:45:45 +02:00
// Not supported.
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : window_move_to_foreground ( WindowID p_window ) {
2020-05-01 14:45:45 +02:00
// Not supported.
}
2023-06-15 10:53:31 +03:00
bool DisplayServerWeb : : window_is_focused ( WindowID p_window ) const {
return true ;
}
2022-08-28 20:27:45 +02:00
bool DisplayServerWeb : : window_can_draw ( WindowID p_window ) const {
2020-05-01 14:45:45 +02:00
return true ;
}
2022-08-28 20:27:45 +02:00
bool DisplayServerWeb : : can_any_window_draw ( ) const {
2020-05-01 14:45:45 +02:00
return true ;
}
2023-05-31 18:23:06 -06:00
DisplayServer : : VSyncMode DisplayServerWeb : : window_get_vsync_mode ( WindowID p_vsync_mode ) const {
return DisplayServer : : VSYNC_ENABLED ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : process_events ( ) {
2024-05-08 10:28:55 +03:00
process_keys ( ) ;
2021-09-12 19:07:44 +02:00
Input : : get_singleton ( ) - > flush_buffered_events ( ) ;
2021-09-12 13:01:15 +02:00
if ( godot_js_input_gamepad_sample ( ) = = OK ) {
2020-05-01 14:45:45 +02:00
process_joypads ( ) ;
2024-05-08 10:28:55 +03:00
}
}
2023-07-11 11:17:35 +03:00
2024-05-08 10:28:55 +03:00
void DisplayServerWeb : : process_keys ( ) {
for ( int i = 0 ; i < key_event_pos ; i + + ) {
const DisplayServerWeb : : KeyEvent & ke = key_event_buffer [ i ] ;
2023-07-11 11:17:35 +03:00
2024-05-08 10:28:55 +03:00
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 ) ;
ev - > set_location ( ke . location ) ;
if ( ke . raw ) {
dom2godot_mod ( ev , ke . mod , ke . keycode ) ;
2023-07-11 11:17:35 +03:00
}
2024-05-08 10:28:55 +03:00
Input : : get_singleton ( ) - > parse_input_event ( ev ) ;
2020-12-27 14:15:43 +01:00
}
2024-05-08 10:28:55 +03:00
key_event_pos = 0 ;
2020-05-01 14:45:45 +02:00
}
2022-08-28 20:27:45 +02:00
int DisplayServerWeb : : get_current_video_driver ( ) const {
2020-05-01 14:45:45 +02:00
return 1 ;
}
2022-08-28 20:27:45 +02:00
bool DisplayServerWeb : : get_swap_cancel_ok ( ) {
2020-07-27 13:53:03 +02:00
return swap_cancel_ok ;
}
2022-08-28 20:27:45 +02:00
void DisplayServerWeb : : swap_buffers ( ) {
2021-10-25 19:16:40 +02:00
# ifdef GLES3_ENABLED
if ( webgl_ctx ) {
emscripten_webgl_commit_frame ( ) ;
}
# endif
2020-05-01 14:45:45 +02:00
}