2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* os_javascript.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2020-01-01 11:16:22 +01:00
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 02:10:30 +01:00
/* */
/* 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. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "os_javascript.h"
2017-01-16 19:19:45 +01:00
2018-09-11 18:13:45 +02:00
# include "core/io/file_access_buffered_fa.h"
2020-01-19 11:44:19 +01:00
# include "core/io/json.h"
2018-10-01 21:35:55 +02:00
# include "drivers/gles2/rasterizer_gles2.h"
# include "drivers/gles3/rasterizer_gles3.h"
# include "drivers/unix/dir_access_unix.h"
# include "drivers/unix/file_access_unix.h"
2014-02-10 02:10:30 +01:00
# include "main/main.h"
2017-03-05 16:44:50 +01:00
# include "servers/visual/visual_server_raster.h"
2020-01-19 11:44:19 +01:00
# ifndef NO_THREADS
# include "servers/visual/visual_server_wrap_mt.h"
# endif
2014-02-10 02:10:30 +01:00
2020-12-05 00:43:02 +01:00
# include <dlfcn.h>
2017-01-16 19:19:45 +01:00
# include <emscripten.h>
2018-09-16 19:37:36 +02:00
# include <png.h>
2017-03-05 16:44:50 +01:00
# include <stdlib.h>
2017-01-16 19:19:45 +01:00
2018-07-08 02:23:19 +02:00
# include "dom_keys.inc"
2020-10-17 23:31:30 +02:00
# include "godot_js.h"
2018-07-08 02:23:19 +02:00
2017-04-24 21:41:39 +02:00
# define DOM_BUTTON_LEFT 0
# define DOM_BUTTON_MIDDLE 1
# define DOM_BUTTON_RIGHT 2
2018-07-05 16:50:30 +02:00
# define DOM_BUTTON_XBUTTON1 3
# define DOM_BUTTON_XBUTTON2 4
2017-04-21 05:01:31 +02:00
2020-10-17 23:31:30 +02:00
// Quit
void OS_JavaScript : : request_quit_callback ( ) {
OS_JavaScript * os = get_singleton ( ) ;
if ( os & & os - > get_main_loop ( ) ) {
os - > get_main_loop ( ) - > notification ( MainLoop : : NOTIFICATION_WM_QUIT_REQUEST ) ;
}
}
2014-02-10 02:10:30 +01:00
2020-10-17 23:31:30 +02:00
// Files drop (implemented in JS for now).
void OS_JavaScript : : drop_files_callback ( char * * p_filev , int p_filec ) {
OS_JavaScript * os = get_singleton ( ) ;
if ( ! os | | ! os - > get_main_loop ( ) ) {
return ;
}
Vector < String > files ;
for ( int i = 0 ; i < p_filec ; i + + ) {
files . push_back ( String : : utf8 ( p_filev [ i ] ) ) ;
}
os - > get_main_loop ( ) - > drop_files ( files ) ;
2014-02-10 02:10:30 +01:00
}
2020-10-17 23:31:30 +02:00
void OS_JavaScript : : send_notification_callback ( int p_notification ) {
2014-02-10 02:10:30 +01:00
2020-10-17 23:31:30 +02:00
OS_JavaScript * os = get_singleton ( ) ;
if ( ! os ) {
return ;
}
if ( p_notification = = MainLoop : : NOTIFICATION_WM_MOUSE_ENTER | | p_notification = = MainLoop : : NOTIFICATION_WM_MOUSE_EXIT ) {
os - > cursor_inside_canvas = p_notification = = MainLoop : : NOTIFICATION_WM_MOUSE_ENTER ;
}
MainLoop * loop = os - > get_main_loop ( ) ;
if ( loop ) {
loop - > notification ( p_notification ) ;
}
2014-02-10 02:10:30 +01:00
}
2020-10-17 23:31:30 +02:00
// Window (canvas)
Point2 OS_JavaScript : : compute_position_in_canvas ( int x , int y ) {
OS_JavaScript * os = get_singleton ( ) ;
int canvas_x ;
int canvas_y ;
godot_js_display_canvas_bounding_rect_position_get ( & canvas_x , & canvas_y ) ;
2019-06-14 22:37:21 +02:00
int canvas_width ;
int canvas_height ;
2020-10-17 23:31:30 +02:00
emscripten_get_canvas_element_size ( os - > canvas_id , & canvas_width , & canvas_height ) ;
2019-06-14 22:37:21 +02:00
double element_width ;
double element_height ;
2020-10-17 23:31:30 +02:00
emscripten_get_element_css_size ( os - > canvas_id , & element_width , & element_height ) ;
2019-06-14 22:37:21 +02:00
2020-01-17 21:25:14 +01:00
return Point2 ( ( int ) ( canvas_width / element_width * ( x - canvas_x ) ) ,
( int ) ( canvas_height / element_height * ( y - canvas_y ) ) ) ;
2019-06-14 22:37:21 +02:00
}
2020-06-29 18:54:20 +02:00
bool OS_JavaScript : : check_size_force_redraw ( ) {
2020-06-15 12:19:07 +02:00
int canvas_width ;
int canvas_height ;
2020-10-17 23:31:30 +02:00
emscripten_get_canvas_element_size ( canvas_id , & canvas_width , & canvas_height ) ;
2020-06-29 18:54:20 +02:00
if ( last_width ! = canvas_width | | last_height ! = canvas_height ) {
last_width = canvas_width ;
last_height = canvas_height ;
2020-07-30 17:10:16 +02:00
// Update the framebuffer size for redraw.
2020-10-17 23:31:30 +02:00
emscripten_set_canvas_element_size ( canvas_id , canvas_width , canvas_height ) ;
2020-06-29 18:54:20 +02:00
return true ;
}
return false ;
2020-06-15 12:19:07 +02:00
}
2018-07-08 02:23:19 +02:00
EM_BOOL OS_JavaScript : : fullscreen_change_callback ( int p_event_type , const EmscriptenFullscreenChangeEvent * p_event , void * p_user_data ) {
2016-11-26 13:13:16 +01:00
2018-07-08 02:23:19 +02:00
OS_JavaScript * os = get_singleton ( ) ;
// Empty ID is canvas.
String target_id = String : : utf8 ( p_event - > id ) ;
2020-10-17 23:31:30 +02:00
if ( target_id . empty ( ) | | target_id = = String : : utf8 ( os - > canvas_id ) ) {
2018-07-08 02:23:19 +02:00
// This event property is the only reliable data on
// browser fullscreen state.
os - > video_mode . fullscreen = p_event - > isFullscreen ;
2018-08-12 15:39:32 +02:00
if ( os - > video_mode . fullscreen ) {
os - > entering_fullscreen = false ;
} else {
// Restoring maximized window now will cause issues,
// so delay until main_loop_iterate.
os - > just_exited_fullscreen = true ;
}
2016-11-26 13:13:16 +01:00
}
return false ;
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : set_video_mode ( const VideoMode & p_video_mode , int p_screen ) {
2017-07-22 15:43:05 +02:00
2018-07-08 02:23:19 +02:00
video_mode = p_video_mode ;
2017-07-22 15:43:05 +02:00
}
2018-07-08 02:23:19 +02:00
OS : : VideoMode OS_JavaScript : : get_video_mode ( int p_screen ) const {
2017-07-22 15:43:05 +02:00
2018-07-08 02:23:19 +02:00
return video_mode ;
2017-07-22 15:43:05 +02:00
}
2018-07-08 02:23:19 +02:00
Size2 OS_JavaScript : : get_screen_size ( int p_screen ) const {
2017-07-22 15:43:05 +02:00
2018-07-08 02:23:19 +02:00
EmscriptenFullscreenChangeEvent ev ;
EMSCRIPTEN_RESULT result = emscripten_get_fullscreen_status ( & ev ) ;
ERR_FAIL_COND_V ( result ! = EMSCRIPTEN_RESULT_SUCCESS , Size2 ( ) ) ;
return Size2 ( ev . screenWidth , ev . screenHeight ) ;
2017-07-22 15:43:05 +02:00
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : set_window_size ( const Size2 p_size ) {
2017-04-21 05:01:31 +02:00
2018-07-08 02:23:19 +02:00
windowed_size = p_size ;
2018-08-12 15:39:32 +02:00
if ( video_mode . fullscreen ) {
2018-07-08 02:23:19 +02:00
window_maximized = false ;
set_window_fullscreen ( false ) ;
2017-07-22 15:43:05 +02:00
} else {
2018-08-12 15:39:32 +02:00
if ( window_maximized ) {
emscripten_exit_soft_fullscreen ( ) ;
window_maximized = false ;
}
2020-10-17 23:31:30 +02:00
double scale = godot_js_display_pixel_ratio_get ( ) ;
2020-11-30 10:54:51 +01:00
emscripten_set_canvas_element_size ( canvas_id , p_size . x , p_size . y ) ;
emscripten_set_element_css_size ( canvas_id , p_size . x / scale , p_size . y / scale ) ;
2017-07-22 15:43:05 +02:00
}
2017-04-21 05:01:31 +02:00
}
2018-07-08 02:23:19 +02:00
Size2 OS_JavaScript : : get_window_size ( ) const {
2017-04-21 05:01:31 +02:00
2018-10-02 02:30:27 +02:00
int canvas [ 2 ] ;
2020-10-17 23:31:30 +02:00
emscripten_get_canvas_element_size ( canvas_id , canvas , canvas + 1 ) ;
2018-07-08 02:23:19 +02:00
return Size2 ( canvas [ 0 ] , canvas [ 1 ] ) ;
2017-04-21 05:01:31 +02:00
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : set_window_maximized ( bool p_enabled ) {
2017-04-21 05:01:31 +02:00
2020-09-17 15:04:51 +02:00
# ifndef TOOLS_ENABLED
2018-08-12 15:39:32 +02:00
if ( video_mode . fullscreen ) {
window_maximized = p_enabled ;
2018-07-08 02:23:19 +02:00
set_window_fullscreen ( false ) ;
2018-08-12 15:39:32 +02:00
} else if ( ! p_enabled ) {
emscripten_exit_soft_fullscreen ( ) ;
window_maximized = false ;
} else if ( ! window_maximized ) {
// Prevent calling emscripten_enter_soft_fullscreen mutltiple times,
// this would hide page elements permanently.
2018-07-08 02:23:19 +02:00
EmscriptenFullscreenStrategy strategy ;
strategy . scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH ;
strategy . canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF ;
strategy . filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT ;
strategy . canvasResizedCallback = NULL ;
2020-10-17 23:31:30 +02:00
emscripten_enter_soft_fullscreen ( canvas_id , & strategy ) ;
2018-08-12 15:39:32 +02:00
window_maximized = p_enabled ;
2018-07-08 02:23:19 +02:00
}
2020-09-17 15:04:51 +02:00
# endif
2018-07-08 02:23:19 +02:00
}
2017-04-21 05:01:31 +02:00
2018-07-08 02:23:19 +02:00
bool OS_JavaScript : : is_window_maximized ( ) const {
2017-04-21 05:01:31 +02:00
2018-07-08 02:23:19 +02:00
return window_maximized ;
2017-04-21 05:01:31 +02:00
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : set_window_fullscreen ( bool p_enabled ) {
2017-04-24 21:41:39 +02:00
2018-08-12 15:39:32 +02:00
if ( p_enabled = = video_mode . fullscreen ) {
2018-07-08 02:23:19 +02:00
return ;
}
2017-04-24 21:41:39 +02:00
2018-08-12 15:39:32 +02:00
// Just request changes here, if successful, logic continues in
// fullscreen_change_callback.
2018-07-08 02:23:19 +02:00
if ( p_enabled ) {
if ( window_maximized ) {
2018-08-12 15:39:32 +02:00
// Soft fullsreen during real fullscreen can cause issues, so exit.
// This must be called before requesting full screen.
emscripten_exit_soft_fullscreen ( ) ;
2018-07-08 02:23:19 +02:00
}
EmscriptenFullscreenStrategy strategy ;
strategy . scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH ;
strategy . canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF ;
strategy . filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT ;
strategy . canvasResizedCallback = NULL ;
2020-10-17 23:31:30 +02:00
EMSCRIPTEN_RESULT result = emscripten_request_fullscreen_strategy ( canvas_id , false , & strategy ) ;
2019-08-09 06:49:33 +02:00
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. " ) ;
2018-08-12 15:39:32 +02:00
// Not fullscreen yet, so prevent "windowed" canvas dimensions from
// being overwritten.
entering_fullscreen = true ;
2018-07-08 02:23:19 +02:00
} else {
2018-08-12 15:39:32 +02:00
// No logic allowed here, since exiting w/ ESC key won't use this function.
ERR_FAIL_COND ( emscripten_exit_fullscreen ( ) ! = EMSCRIPTEN_RESULT_SUCCESS ) ;
2018-07-08 02:23:19 +02:00
}
}
2017-04-24 21:41:39 +02:00
2018-07-08 02:23:19 +02:00
bool OS_JavaScript : : is_window_fullscreen ( ) const {
2017-04-24 21:41:39 +02:00
2018-07-08 02:23:19 +02:00
return video_mode . fullscreen ;
2017-04-24 21:41:39 +02:00
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : get_fullscreen_mode_list ( List < VideoMode > * p_list , int p_screen ) const {
2017-04-24 21:41:39 +02:00
2018-07-08 02:23:19 +02:00
Size2 screen = get_screen_size ( ) ;
p_list - > push_back ( OS : : VideoMode ( screen . width , screen . height , true ) ) ;
}
2017-04-24 21:41:39 +02:00
2020-02-07 01:18:11 +01:00
bool OS_JavaScript : : get_window_per_pixel_transparency_enabled ( ) const {
if ( ! is_layered_allowed ( ) ) {
return false ;
}
return transparency_enabled ;
}
void OS_JavaScript : : set_window_per_pixel_transparency_enabled ( bool p_enabled ) {
if ( ! is_layered_allowed ( ) ) {
return ;
}
transparency_enabled = p_enabled ;
}
2018-07-08 02:23:19 +02:00
// Keys
2017-04-24 21:41:39 +02:00
2018-07-08 02:23:19 +02:00
template < typename T >
static void dom2godot_mod ( T * emscripten_event_ptr , Ref < InputEventWithModifiers > godot_event ) {
2017-04-24 21:41:39 +02:00
2018-07-08 02:23:19 +02:00
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 ) ;
2017-04-24 21:41:39 +02:00
}
2018-07-08 02:23:19 +02:00
static Ref < InputEventKey > setup_key_event ( const EmscriptenKeyboardEvent * emscripten_event ) {
2016-04-16 00:19:02 +02:00
2017-06-19 15:43:46 +02:00
Ref < InputEventKey > ev ;
ev . instance ( ) ;
ev - > set_echo ( emscripten_event - > repeat ) ;
dom2godot_mod ( emscripten_event , ev ) ;
2020-06-04 19:59:45 +02:00
ev - > set_scancode ( dom_code2godot_scancode ( emscripten_event - > code , emscripten_event - > key ) ) ;
2016-04-16 00:19:02 +02:00
String unicode = String : : utf8 ( emscripten_event - > key ) ;
2018-07-08 02:23:19 +02:00
// Check if empty or multi-character (e.g. `CapsLock`).
2017-03-05 16:44:50 +01:00
if ( unicode . length ( ) ! = 1 ) {
2018-07-08 02:23:19 +02:00
// Might be empty as well, but better than nonsense.
2016-04-16 00:19:02 +02:00
unicode = String : : utf8 ( emscripten_event - > charValue ) ;
}
2017-03-05 16:44:50 +01:00
if ( unicode . length ( ) = = 1 ) {
2017-06-19 15:43:46 +02:00
ev - > set_unicode ( unicode [ 0 ] ) ;
2016-04-16 00:19:02 +02:00
}
return ev ;
}
2018-07-08 02:23:19 +02:00
EM_BOOL OS_JavaScript : : keydown_callback ( int p_event_type , const EmscriptenKeyboardEvent * p_event , void * p_user_data ) {
2016-04-16 00:19:02 +02:00
2018-07-08 02:23:19 +02:00
OS_JavaScript * os = get_singleton ( ) ;
Ref < InputEventKey > ev = setup_key_event ( p_event ) ;
2017-06-19 15:43:46 +02:00
ev - > set_pressed ( true ) ;
if ( ev - > get_unicode ( ) = = 0 & & keycode_has_unicode ( ev - > get_scancode ( ) ) ) {
2018-07-08 02:23:19 +02:00
// Defer to keypress event for legacy unicode retrieval.
os - > deferred_key_event = ev ;
// Do not suppress keypress event.
return false ;
2016-04-19 14:15:20 +02:00
}
2018-07-08 02:23:19 +02:00
os - > input - > parse_input_event ( ev ) ;
2019-01-20 14:25:15 +01:00
// Resume audio context after input in case autoplay was denied.
2020-06-29 18:51:53 +02:00
os - > resume_audio ( ) ;
2016-04-19 14:15:20 +02:00
return true ;
}
2018-07-08 02:23:19 +02:00
EM_BOOL OS_JavaScript : : keypress_callback ( int p_event_type , const EmscriptenKeyboardEvent * p_event , void * p_user_data ) {
2016-04-19 14:15:20 +02:00
2018-07-08 02:23:19 +02:00
OS_JavaScript * os = get_singleton ( ) ;
os - > deferred_key_event - > set_unicode ( p_event - > charCode ) ;
os - > input - > parse_input_event ( os - > deferred_key_event ) ;
2016-04-19 14:15:20 +02:00
return true ;
2016-04-16 00:19:02 +02:00
}
2018-07-08 02:23:19 +02:00
EM_BOOL OS_JavaScript : : keyup_callback ( int p_event_type , const EmscriptenKeyboardEvent * p_event , void * p_user_data ) {
2016-04-16 00:19:02 +02:00
2018-07-08 02:23:19 +02:00
Ref < InputEventKey > ev = setup_key_event ( p_event ) ;
2017-06-19 15:43:46 +02:00
ev - > set_pressed ( false ) ;
2018-07-08 02:23:19 +02:00
get_singleton ( ) - > input - > parse_input_event ( ev ) ;
2017-05-20 17:38:03 +02:00
return ev - > get_scancode ( ) ! = KEY_UNKNOWN & & ev - > get_scancode ( ) ! = 0 ;
2016-04-16 00:19:02 +02:00
}
2018-07-08 02:23:19 +02:00
// Mouse
Point2 OS_JavaScript : : get_mouse_position ( ) const {
return input - > get_mouse_position ( ) ;
2016-01-21 02:23:15 +01:00
}
2018-07-08 02:23:19 +02:00
int OS_JavaScript : : get_mouse_button_state ( ) const {
2018-03-21 15:51:44 +01:00
2018-07-08 02:23:19 +02:00
return input - > get_mouse_button_mask ( ) ;
2017-07-22 15:43:05 +02:00
}
2018-07-08 02:23:19 +02:00
EM_BOOL OS_JavaScript : : mouse_button_callback ( int p_event_type , const EmscriptenMouseEvent * p_event , void * p_user_data ) {
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
OS_JavaScript * os = get_singleton ( ) ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
Ref < InputEventMouseButton > ev ;
ev . instance ( ) ;
ev - > set_pressed ( p_event_type = = EMSCRIPTEN_EVENT_MOUSEDOWN ) ;
2020-01-17 21:25:14 +01:00
ev - > set_position ( compute_position_in_canvas ( p_event - > clientX , p_event - > clientY ) ) ;
2018-07-08 02:23:19 +02:00
ev - > set_global_position ( ev - > get_position ( ) ) ;
dom2godot_mod ( p_event , ev ) ;
2020-01-17 14:10:50 +01:00
2018-07-08 02:23:19 +02:00
switch ( p_event - > button ) {
case DOM_BUTTON_LEFT : ev - > set_button_index ( BUTTON_LEFT ) ; break ;
case DOM_BUTTON_MIDDLE : ev - > set_button_index ( BUTTON_MIDDLE ) ; break ;
case DOM_BUTTON_RIGHT : ev - > set_button_index ( BUTTON_RIGHT ) ; break ;
case DOM_BUTTON_XBUTTON1 : ev - > set_button_index ( BUTTON_XBUTTON1 ) ; break ;
case DOM_BUTTON_XBUTTON2 : ev - > set_button_index ( BUTTON_XBUTTON2 ) ; break ;
default : return false ;
2018-03-07 19:53:13 +01:00
}
2014-02-10 02:10:30 +01:00
2018-07-20 18:16:09 +02:00
if ( ev - > is_pressed ( ) ) {
2020-01-17 14:10:50 +01:00
double diff = emscripten_get_now ( ) - os - > last_click_ms ;
2018-07-20 18:16:09 +02:00
if ( ev - > get_button_index ( ) = = os - > last_click_button_index ) {
if ( diff < 400 & & Point2 ( os - > last_click_pos ) . distance_to ( ev - > get_position ( ) ) < 5 ) {
os - > last_click_ms = 0 ;
os - > last_click_pos = Point2 ( - 100 , - 100 ) ;
os - > last_click_button_index = - 1 ;
ev - > set_doubleclick ( true ) ;
}
} else {
os - > last_click_button_index = ev - > get_button_index ( ) ;
}
if ( ! ev - > is_doubleclick ( ) ) {
os - > last_click_ms + = diff ;
os - > last_click_pos = ev - > get_position ( ) ;
}
}
2018-07-08 02:23:19 +02:00
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.
2020-10-17 23:31:30 +02:00
godot_js_display_canvas_focus ( ) ;
2018-07-08 02:23:19 +02:00
mask | = button_flag ;
} else if ( mask & button_flag ) {
mask & = ~ button_flag ;
2017-11-19 15:30:09 +01:00
} else {
2018-07-08 02:23:19 +02:00
// Received release event, but press was outside the canvas, so ignore.
return false ;
2017-11-19 15:30:09 +01:00
}
2018-07-08 02:23:19 +02:00
ev - > set_button_mask ( mask ) ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
os - > input - > parse_input_event ( ev ) ;
2019-01-20 14:25:15 +01:00
// Resume audio context after input in case autoplay was denied.
2020-06-29 18:51:53 +02:00
os - > resume_audio ( ) ;
2018-07-08 02:23:19 +02:00
// Prevent multi-click text selection and wheel-click scrolling anchor.
// Context menu is prevented through contextmenu event.
return true ;
2014-02-10 02:10:30 +01:00
}
2018-07-08 02:23:19 +02:00
EM_BOOL OS_JavaScript : : mousemove_callback ( int p_event_type , const EmscriptenMouseEvent * p_event , void * p_user_data ) {
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
OS_JavaScript * os = get_singleton ( ) ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
int input_mask = os - > input - > get_mouse_button_mask ( ) ;
2020-01-17 21:25:14 +01:00
Point2 pos = compute_position_in_canvas ( p_event - > clientX , p_event - > clientY ) ;
2018-07-08 02:23:19 +02:00
// For motion outside the canvas, only read mouse movement if dragging
// started inside the canvas; imitating desktop app behaviour.
2020-10-17 23:31:30 +02:00
if ( ! os - > cursor_inside_canvas & & ! input_mask )
2018-07-08 02:23:19 +02:00
return false ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
Ref < InputEventMouseMotion > ev ;
ev . instance ( ) ;
dom2godot_mod ( p_event , ev ) ;
ev - > set_button_mask ( input_mask ) ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
ev - > set_position ( pos ) ;
ev - > set_global_position ( ev - > get_position ( ) ) ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
ev - > set_relative ( Vector2 ( p_event - > movementX , p_event - > movementY ) ) ;
os - > input - > set_mouse_position ( ev - > get_position ( ) ) ;
ev - > set_speed ( os - > input - > get_last_mouse_speed ( ) ) ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
os - > input - > parse_input_event ( ev ) ;
// Don't suppress mouseover/-leave events.
return false ;
2014-02-10 02:10:30 +01:00
}
2017-04-29 21:35:31 +02:00
static const char * godot2dom_cursor ( OS : : CursorShape p_shape ) {
switch ( p_shape ) {
case OS : : CURSOR_ARROW :
default :
return " auto " ;
case OS : : CURSOR_IBEAM : return " text " ;
case OS : : CURSOR_POINTING_HAND : return " pointer " ;
case OS : : CURSOR_CROSS : return " crosshair " ;
case OS : : CURSOR_WAIT : return " progress " ;
case OS : : CURSOR_BUSY : return " wait " ;
case OS : : CURSOR_DRAG : return " grab " ;
case OS : : CURSOR_CAN_DROP : return " grabbing " ;
case OS : : CURSOR_FORBIDDEN : return " no-drop " ;
case OS : : CURSOR_VSIZE : return " ns-resize " ;
case OS : : CURSOR_HSIZE : return " ew-resize " ;
case OS : : CURSOR_BDIAGSIZE : return " nesw-resize " ;
case OS : : CURSOR_FDIAGSIZE : return " nwse-resize " ;
case OS : : CURSOR_MOVE : return " move " ;
case OS : : CURSOR_VSPLIT : return " row-resize " ;
case OS : : CURSOR_HSPLIT : return " col-resize " ;
case OS : : CURSOR_HELP : return " help " ;
}
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : set_cursor_shape ( CursorShape p_shape ) {
ERR_FAIL_INDEX ( p_shape , CURSOR_MAX ) ;
2020-10-17 23:31:30 +02:00
if ( cursor_shape = = p_shape ) {
return ;
2018-11-28 17:04:37 +01:00
}
2018-07-08 02:23:19 +02:00
cursor_shape = p_shape ;
2020-10-17 23:31:30 +02:00
godot_js_display_cursor_set_shape ( godot2dom_cursor ( cursor_shape ) ) ;
2018-07-08 02:23:19 +02:00
}
void OS_JavaScript : : set_custom_mouse_cursor ( const RES & p_cursor , CursorShape p_shape , const Vector2 & p_hotspot ) {
2018-11-28 17:04:37 +01:00
if ( p_cursor . is_valid ( ) ) {
2019-07-08 22:37:18 +02:00
2018-11-28 17:04:37 +01:00
Ref < Texture > texture = p_cursor ;
Ref < AtlasTexture > atlas_texture = p_cursor ;
Ref < Image > image ;
Size2 texture_size ;
Rect2 atlas_rect ;
if ( texture . is_valid ( ) ) {
image = texture - > get_data ( ) ;
2019-01-27 17:39:16 +01:00
if ( image . is_valid ( ) ) {
image - > duplicate ( ) ;
}
2018-11-28 17:04:37 +01:00
}
if ( ! image . is_valid ( ) & & atlas_texture . is_valid ( ) ) {
texture = atlas_texture - > get_atlas ( ) ;
atlas_rect . size . width = texture - > get_width ( ) ;
atlas_rect . size . height = texture - > get_height ( ) ;
atlas_rect . position . x = atlas_texture - > get_region ( ) . position . x ;
atlas_rect . position . y = atlas_texture - > get_region ( ) . position . y ;
texture_size . width = atlas_texture - > get_region ( ) . size . x ;
texture_size . height = atlas_texture - > get_region ( ) . size . y ;
} else if ( image . is_valid ( ) ) {
texture_size . width = texture - > get_width ( ) ;
texture_size . height = texture - > get_height ( ) ;
}
ERR_FAIL_COND ( ! texture . is_valid ( ) ) ;
ERR_FAIL_COND ( p_hotspot . x < 0 | | p_hotspot . y < 0 ) ;
ERR_FAIL_COND ( texture_size . width > 256 | | texture_size . height > 256 ) ;
ERR_FAIL_COND ( p_hotspot . x > texture_size . width | | p_hotspot . y > texture_size . height ) ;
image = texture - > get_data ( ) ;
ERR_FAIL_COND ( ! image . is_valid ( ) ) ;
2019-01-27 17:39:16 +01:00
image = image - > duplicate ( ) ;
2018-11-28 17:04:37 +01:00
if ( atlas_texture . is_valid ( ) )
image - > crop_from_point (
atlas_rect . position . x ,
atlas_rect . position . y ,
texture_size . width ,
texture_size . height ) ;
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 ;
PoolByteArray png ;
size_t len ;
PoolByteArray : : Read r = image - > get_data ( ) . read ( ) ;
ERR_FAIL_COND ( ! png_image_write_get_memory_size ( png_meta , len , 0 , r . ptr ( ) , 0 , NULL ) ) ;
png . resize ( len ) ;
PoolByteArray : : Write w = png . write ( ) ;
ERR_FAIL_COND ( ! png_image_write_to_memory ( & png_meta , w . ptr ( ) , & len , 0 , r . ptr ( ) , 0 , NULL ) ) ;
w = PoolByteArray : : Write ( ) ;
r = png . read ( ) ;
2020-10-17 23:31:30 +02:00
godot_js_display_cursor_set_custom_shape ( godot2dom_cursor ( p_shape ) , r . ptr ( ) , len , p_hotspot . x , p_hotspot . y ) ;
2018-11-28 17:04:37 +01:00
r = PoolByteArray : : Read ( ) ;
2020-10-17 23:31:30 +02:00
} else {
godot_js_display_cursor_set_custom_shape ( godot2dom_cursor ( p_shape ) , NULL , 0 , 0 , 0 ) ;
2018-11-28 17:04:37 +01:00
}
2018-07-08 02:23:19 +02:00
}
2017-04-29 04:54:48 +02:00
void OS_JavaScript : : set_mouse_mode ( OS : : MouseMode p_mode ) {
2014-02-10 02:10:30 +01:00
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_MSG ( p_mode = = MOUSE_MODE_CONFINED , " MOUSE_MODE_CONFINED is not supported for the HTML5 platform. " ) ;
2017-04-29 04:54:48 +02:00
if ( p_mode = = get_mouse_mode ( ) )
return ;
if ( p_mode = = MOUSE_MODE_VISIBLE ) {
2020-10-17 23:31:30 +02:00
godot_js_display_cursor_set_visible ( 1 ) ;
2017-04-29 04:54:48 +02:00
emscripten_exit_pointerlock ( ) ;
} else if ( p_mode = = MOUSE_MODE_HIDDEN ) {
2020-10-17 23:31:30 +02:00
godot_js_display_cursor_set_visible ( 0 ) ;
2017-04-29 04:54:48 +02:00
emscripten_exit_pointerlock ( ) ;
} else if ( p_mode = = MOUSE_MODE_CAPTURED ) {
2020-10-17 23:31:30 +02:00
godot_js_display_cursor_set_visible ( 1 ) ;
EMSCRIPTEN_RESULT result = emscripten_request_pointerlock ( canvas_id , false ) ;
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_MSG ( result = = EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED , " MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback. " ) ;
ERR_FAIL_COND_MSG ( result ! = EMSCRIPTEN_RESULT_SUCCESS , " MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback. " ) ;
2017-04-29 04:54:48 +02:00
}
}
OS : : MouseMode OS_JavaScript : : get_mouse_mode ( ) const {
2020-10-17 23:31:30 +02:00
if ( godot_js_display_cursor_is_hidden ( ) )
2017-04-29 04:54:48 +02:00
return MOUSE_MODE_HIDDEN ;
EmscriptenPointerlockChangeEvent ev ;
emscripten_get_pointerlock_status ( & ev ) ;
2020-10-17 23:31:30 +02:00
return ( ev . isActive & & String : : utf8 ( ev . id ) = = String : : utf8 ( canvas_id ) ) ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE ;
2014-02-10 02:10:30 +01:00
}
2016-11-23 23:53:38 +01:00
2018-07-08 02:23:19 +02:00
// Wheel
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
EM_BOOL OS_JavaScript : : wheel_callback ( int p_event_type , const EmscriptenWheelEvent * p_event , void * p_user_data ) {
2016-11-23 23:53:38 +01:00
2018-07-08 02:23:19 +02:00
ERR_FAIL_COND_V ( p_event_type ! = EMSCRIPTEN_EVENT_WHEEL , false ) ;
2020-10-17 23:31:30 +02:00
OS_JavaScript * os = get_singleton ( ) ;
if ( ! godot_js_display_canvas_is_focused ( ) ) {
if ( os - > cursor_inside_canvas ) {
godot_js_display_canvas_focus ( ) ;
2018-07-08 02:23:19 +02:00
} else {
return false ;
}
}
2014-02-10 02:10:30 +01:00
2020-10-17 23:31:30 +02:00
InputDefault * input = os - > input ;
2018-07-08 02:23:19 +02:00
Ref < InputEventMouseButton > ev ;
ev . instance ( ) ;
ev - > set_position ( input - > get_mouse_position ( ) ) ;
ev - > set_global_position ( ev - > get_position ( ) ) ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
ev - > set_shift ( input - > is_key_pressed ( KEY_SHIFT ) ) ;
ev - > set_alt ( input - > is_key_pressed ( KEY_ALT ) ) ;
ev - > set_control ( input - > is_key_pressed ( KEY_CONTROL ) ) ;
ev - > set_metakey ( input - > is_key_pressed ( KEY_META ) ) ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
if ( p_event - > deltaY < 0 )
ev - > set_button_index ( BUTTON_WHEEL_UP ) ;
else if ( p_event - > deltaY > 0 )
ev - > set_button_index ( BUTTON_WHEEL_DOWN ) ;
else if ( p_event - > deltaX > 0 )
ev - > set_button_index ( BUTTON_WHEEL_LEFT ) ;
else if ( p_event - > deltaX < 0 )
ev - > set_button_index ( BUTTON_WHEEL_RIGHT ) ;
else
return false ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
// Different browsers give wildly different delta values, and we can't
// interpret deltaMode, so use default value for wheel events' factor.
2014-02-10 02:10:30 +01:00
2018-07-08 15:12:13 +02:00
int button_flag = 1 < < ( ev - > get_button_index ( ) - 1 ) ;
2018-07-08 02:23:19 +02:00
ev - > set_pressed ( true ) ;
2018-07-08 15:12:13 +02:00
ev - > set_button_mask ( input - > get_mouse_button_mask ( ) | button_flag ) ;
2018-07-08 02:23:19 +02:00
input - > parse_input_event ( ev ) ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
ev - > set_pressed ( false ) ;
2018-07-08 15:12:13 +02:00
ev - > set_button_mask ( input - > get_mouse_button_mask ( ) & ~ button_flag ) ;
2018-07-08 02:23:19 +02:00
input - > parse_input_event ( ev ) ;
return true ;
2014-02-10 02:10:30 +01:00
}
2018-07-08 02:23:19 +02:00
// Touch
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
bool OS_JavaScript : : has_touchscreen_ui_hint ( ) const {
2020-10-17 23:31:30 +02:00
return godot_js_display_touchscreen_is_available ( ) ;
2016-11-26 13:13:16 +01:00
}
2018-07-08 02:23:19 +02:00
EM_BOOL OS_JavaScript : : touch_press_callback ( int p_event_type , const EmscriptenTouchEvent * p_event , void * p_user_data ) {
2016-11-26 13:13:16 +01:00
2018-07-08 02:23:19 +02:00
OS_JavaScript * os = get_singleton ( ) ;
Ref < InputEventScreenTouch > ev ;
ev . instance ( ) ;
int lowest_id_index = - 1 ;
for ( int i = 0 ; i < p_event - > numTouches ; + + i ) {
2016-11-26 13:13:16 +01:00
2018-07-08 02:23:19 +02:00
const EmscriptenTouchPoint & touch = p_event - > touches [ i ] ;
if ( lowest_id_index = = - 1 | | touch . identifier < p_event - > touches [ lowest_id_index ] . identifier )
lowest_id_index = i ;
if ( ! touch . isChanged )
continue ;
ev - > set_index ( touch . identifier ) ;
2020-01-17 21:25:14 +01:00
ev - > set_position ( compute_position_in_canvas ( touch . clientX , touch . clientY ) ) ;
2018-07-08 02:23:19 +02:00
os - > touches [ i ] = ev - > get_position ( ) ;
ev - > set_pressed ( p_event_type = = EMSCRIPTEN_EVENT_TOUCHSTART ) ;
2016-11-26 13:13:16 +01:00
2018-07-08 02:23:19 +02:00
os - > input - > parse_input_event ( ev ) ;
2016-11-26 13:13:16 +01:00
}
2019-01-20 14:25:15 +01:00
// Resume audio context after input in case autoplay was denied.
2020-06-29 18:51:53 +02:00
os - > resume_audio ( ) ;
2018-07-08 02:23:19 +02:00
return true ;
2014-02-10 02:10:30 +01:00
}
2015-04-02 19:54:36 +02:00
2018-07-08 02:23:19 +02:00
EM_BOOL OS_JavaScript : : touchmove_callback ( int p_event_type , const EmscriptenTouchEvent * p_event , void * p_user_data ) {
2015-04-02 19:54:36 +02:00
2018-07-08 02:23:19 +02:00
OS_JavaScript * os = get_singleton ( ) ;
Ref < InputEventScreenDrag > ev ;
ev . instance ( ) ;
int lowest_id_index = - 1 ;
for ( int i = 0 ; i < p_event - > numTouches ; + + i ) {
2016-11-26 13:13:16 +01:00
2018-07-08 02:23:19 +02:00
const EmscriptenTouchPoint & touch = p_event - > touches [ i ] ;
if ( lowest_id_index = = - 1 | | touch . identifier < p_event - > touches [ lowest_id_index ] . identifier )
lowest_id_index = i ;
if ( ! touch . isChanged )
continue ;
ev - > set_index ( touch . identifier ) ;
2020-01-17 21:25:14 +01:00
ev - > set_position ( compute_position_in_canvas ( touch . clientX , touch . clientY ) ) ;
2018-07-08 02:23:19 +02:00
Point2 & prev = os - > touches [ i ] ;
ev - > set_relative ( ev - > get_position ( ) - prev ) ;
prev = ev - > get_position ( ) ;
2016-11-26 13:13:16 +01:00
2018-07-08 02:23:19 +02:00
os - > input - > parse_input_event ( ev ) ;
2017-07-25 20:55:44 +02:00
}
2018-07-08 02:23:19 +02:00
return true ;
}
2017-07-25 20:55:44 +02:00
2018-07-08 02:23:19 +02:00
// Gamepad
2017-07-25 20:55:44 +02:00
2018-07-08 02:23:19 +02:00
EM_BOOL OS_JavaScript : : gamepad_change_callback ( int p_event_type , const EmscriptenGamepadEvent * p_event , void * p_user_data ) {
InputDefault * input = get_singleton ( ) - > input ;
if ( p_event_type = = EMSCRIPTEN_EVENT_GAMEPADCONNECTED ) {
String guid = " " ;
if ( String : : utf8 ( p_event - > mapping ) = = " standard " )
guid = " Default HTML5 Gamepad " ;
input - > joy_connection_changed ( p_event - > index , true , String : : utf8 ( p_event - > id ) , guid ) ;
} else {
input - > joy_connection_changed ( p_event - > index , false , " " ) ;
2016-11-26 13:13:16 +01:00
}
2018-07-08 02:23:19 +02:00
return true ;
2016-11-26 13:13:16 +01:00
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : process_joypads ( ) {
2016-11-26 13:13:16 +01:00
2018-07-08 02:23:19 +02:00
int joypad_count = emscripten_get_num_gamepads ( ) ;
for ( int joypad = 0 ; joypad < joypad_count ; joypad + + ) {
EmscriptenGamepadEvent state ;
2018-08-30 02:23:06 +02:00
EMSCRIPTEN_RESULT query_result = emscripten_get_gamepad_status ( joypad , & state ) ;
// Chromium reserves gamepads slots, so NO_DATA is an expected result.
ERR_CONTINUE ( query_result ! = EMSCRIPTEN_RESULT_SUCCESS & &
query_result ! = EMSCRIPTEN_RESULT_NO_DATA ) ;
if ( query_result = = EMSCRIPTEN_RESULT_SUCCESS & & state . connected ) {
2016-11-26 13:13:16 +01:00
2018-07-08 02:23:19 +02:00
int button_count = MIN ( state . numButtons , 18 ) ;
int axis_count = MIN ( state . numAxes , 8 ) ;
for ( int button = 0 ; button < button_count ; button + + ) {
float value = state . analogButton [ button ] ;
if ( String : : utf8 ( state . mapping ) = = " standard " & & ( button = = JOY_ANALOG_L2 | | button = = JOY_ANALOG_R2 ) ) {
InputDefault : : JoyAxis joy_axis ;
joy_axis . min = 0 ;
joy_axis . value = value ;
input - > joy_axis ( joypad , button , joy_axis ) ;
} else {
input - > joy_button ( joypad , button , value ) ;
}
}
for ( int axis = 0 ; axis < axis_count ; axis + + ) {
InputDefault : : JoyAxis joy_axis ;
joy_axis . min = - 1 ;
joy_axis . value = state . axis [ axis ] ;
input - > joy_axis ( joypad , axis , joy_axis ) ;
}
2016-11-26 13:13:16 +01:00
}
}
}
2018-07-08 02:23:19 +02:00
bool OS_JavaScript : : is_joy_known ( int p_device ) {
2016-11-26 13:13:16 +01:00
2018-07-08 02:23:19 +02:00
return input - > is_joy_mapped ( p_device ) ;
2015-04-02 19:54:36 +02:00
}
2018-07-08 02:23:19 +02:00
String OS_JavaScript : : get_joy_guid ( int p_device ) const {
2017-07-25 20:55:44 +02:00
2018-07-08 02:23:19 +02:00
return input - > get_joy_guid_remapped ( p_device ) ;
2017-07-25 20:55:44 +02:00
}
2018-07-08 02:23:19 +02:00
// Video
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
int OS_JavaScript : : get_video_driver_count ( ) const {
return VIDEO_DRIVER_MAX ;
2014-02-10 02:10:30 +01:00
}
2018-07-08 02:23:19 +02:00
const char * OS_JavaScript : : get_video_driver_name ( int p_driver ) const {
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
switch ( p_driver ) {
case VIDEO_DRIVER_GLES3 :
return " GLES3 " ;
case VIDEO_DRIVER_GLES2 :
return " GLES2 " ;
}
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( NULL , " Invalid video driver index: " + itos ( p_driver ) + " . " ) ;
2014-02-10 02:10:30 +01:00
}
2018-07-08 02:23:19 +02:00
// Audio
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
int OS_JavaScript : : get_audio_driver_count ( ) const {
return 1 ;
2014-02-10 02:10:30 +01:00
}
2018-07-08 02:23:19 +02:00
const char * OS_JavaScript : : get_audio_driver_name ( int p_driver ) const {
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
return " JavaScript " ;
2014-02-10 02:10:30 +01:00
}
2019-05-28 12:59:29 +02:00
// Clipboard
2020-10-17 23:31:30 +02:00
void OS_JavaScript : : update_clipboard_callback ( const char * p_text ) {
2019-05-28 22:27:03 +02:00
// Only call set_clipboard from OS (sets local clipboard)
2020-10-17 23:31:30 +02:00
get_singleton ( ) - > OS : : set_clipboard ( p_text ) ;
2019-05-28 22:27:03 +02:00
}
2019-05-28 12:59:29 +02:00
void OS_JavaScript : : set_clipboard ( const String & p_text ) {
OS : : set_clipboard ( p_text ) ;
2020-10-17 23:31:30 +02:00
int err = godot_js_display_clipboard_set ( p_text . utf8 ( ) . get_data ( ) ) ;
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_MSG ( err , " Clipboard API is not supported. " ) ;
2019-05-28 12:59:29 +02:00
}
2019-05-29 21:30:29 +02:00
String OS_JavaScript : : get_clipboard ( ) const {
2020-10-17 23:31:30 +02:00
godot_js_display_clipboard_get ( update_clipboard_callback ) ;
2019-05-29 21:30:29 +02:00
return this - > OS : : get_clipboard ( ) ;
}
2018-07-08 02:23:19 +02:00
// Lifecycle
2018-07-19 23:58:15 +02:00
int OS_JavaScript : : get_current_video_driver ( ) const {
return video_driver_index ;
}
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : initialize_core ( ) {
2017-04-29 21:35:31 +02:00
2018-07-08 02:23:19 +02:00
OS_Unix : : initialize_core ( ) ;
2014-02-10 02:10:30 +01:00
}
2018-07-08 02:23:19 +02:00
Error OS_JavaScript : : initialize ( const VideoMode & p_desired , int p_video_driver , int p_audio_driver ) {
2020-10-17 23:31:30 +02:00
swap_ok_cancel = godot_js_display_is_swap_ok_cancel ( ) = = 1 ;
2020-07-27 13:53:03 +02:00
2018-07-08 02:23:19 +02:00
EmscriptenWebGLContextAttributes attributes ;
emscripten_webgl_init_context_attributes ( & attributes ) ;
2020-02-07 01:18:11 +01:00
attributes . alpha = GLOBAL_GET ( " display/window/per_pixel_transparency/allowed " ) ;
2018-07-08 02:23:19 +02:00
attributes . antialias = false ;
2020-01-19 11:44:19 +01:00
attributes . explicitSwapControl = true ;
2018-07-08 02:23:19 +02:00
ERR_FAIL_INDEX_V ( p_video_driver , VIDEO_DRIVER_MAX , ERR_INVALID_PARAMETER ) ;
2018-08-25 00:04:25 +02:00
2020-02-07 01:18:11 +01:00
if ( p_desired . layered ) {
set_window_per_pixel_transparency_enabled ( true ) ;
}
2020-09-17 15:04:51 +02:00
# ifdef TOOLS_ENABLED
bool gles3 = false ;
# else
2018-08-25 00:04:25 +02:00
bool gles3 = true ;
if ( p_video_driver = = VIDEO_DRIVER_GLES2 ) {
gles3 = false ;
}
2020-09-17 15:04:51 +02:00
# endif
2018-08-25 00:04:25 +02:00
bool gl_initialization_error = false ;
while ( true ) {
if ( gles3 ) {
if ( RasterizerGLES3 : : is_viable ( ) = = OK ) {
attributes . majorVersion = 2 ;
RasterizerGLES3 : : register_config ( ) ;
RasterizerGLES3 : : make_current ( ) ;
break ;
} else {
2019-03-05 13:46:51 +01:00
if ( GLOBAL_GET ( " rendering/quality/driver/fallback_to_gles2 " ) ) {
2018-08-25 00:04:25 +02:00
p_video_driver = VIDEO_DRIVER_GLES2 ;
gles3 = false ;
continue ;
} else {
gl_initialization_error = true ;
break ;
}
}
} else {
if ( RasterizerGLES2 : : is_viable ( ) = = OK ) {
attributes . majorVersion = 1 ;
RasterizerGLES2 : : register_config ( ) ;
RasterizerGLES2 : : make_current ( ) ;
break ;
} else {
gl_initialization_error = true ;
break ;
}
}
2018-07-08 02:23:19 +02:00
}
2018-07-19 23:58:15 +02:00
2020-10-17 23:31:30 +02:00
webgl_ctx = emscripten_webgl_create_context ( canvas_id , & attributes ) ;
2020-01-19 11:44:19 +01:00
if ( emscripten_webgl_make_context_current ( webgl_ctx ) ! = EMSCRIPTEN_RESULT_SUCCESS ) {
2018-08-25 00:04:25 +02:00
gl_initialization_error = true ;
}
if ( gl_initialization_error ) {
OS : : get_singleton ( ) - > alert ( " Your browser does not support any of the supported WebGL versions. \n "
" Please update your browser version. " ,
" Unable to initialize Video driver " ) ;
return ERR_UNAVAILABLE ;
}
video_driver_index = p_video_driver ;
2018-07-08 02:23:19 +02:00
video_mode = p_desired ;
2019-01-23 03:39:45 +01:00
// fullscreen_change_callback will correct this if the request is successful.
2018-07-08 02:23:19 +02:00
video_mode . fullscreen = false ;
2019-01-23 03:39:45 +01:00
// Emscripten only attempts fullscreen requests if the user input callback
// was registered through one its own functions, so request manually for
// start-up fullscreen.
if ( p_desired . fullscreen ) {
2020-10-17 23:31:30 +02:00
godot_js_display_window_request_fullscreen ( ) ;
2019-01-23 03:39:45 +01:00
}
2020-10-17 23:31:30 +02:00
if ( godot_js_config_is_resize_on_start ( ) ) {
2018-07-08 02:23:19 +02:00
set_window_size ( Size2 ( video_mode . width , video_mode . height ) ) ;
} else {
set_window_size ( get_window_size ( ) ) ;
}
AudioDriverManager : : initialize ( p_audio_driver ) ;
2020-01-19 11:44:19 +01:00
visual_server = memnew ( VisualServerRaster ( ) ) ;
# ifndef NO_THREADS
visual_server = memnew ( VisualServerWrapMT ( visual_server , false ) ) ;
# endif
2018-07-08 02:23:19 +02:00
input = memnew ( InputDefault ) ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
EMSCRIPTEN_RESULT result ;
# define EM_CHECK(ev) \
if ( result ! = EMSCRIPTEN_RESULT_SUCCESS ) \
ERR_PRINTS ( " Error while setting " # ev " callback: Code " + itos ( result ) )
# define SET_EM_CALLBACK(target, ev, cb) \
result = emscripten_set_ # # ev # # _callback ( target , NULL , true , & cb ) ; \
EM_CHECK ( ev )
2020-07-30 14:08:30 +02:00
# define SET_EM_WINDOW_CALLBACK(ev, cb) \
result = emscripten_set_ # # ev # # _callback ( EMSCRIPTEN_EVENT_TARGET_WINDOW , NULL , false , & cb ) ; \
EM_CHECK ( ev )
2018-07-08 02:23:19 +02:00
# define SET_EM_CALLBACK_NOTARGET(ev, cb) \
result = emscripten_set_ # # ev # # _callback ( NULL , true , & cb ) ; \
EM_CHECK ( ev )
// These callbacks from Emscripten's html5.h suffice to access most
2020-10-17 23:31:30 +02:00
// JavaScript APIs.
SET_EM_CALLBACK ( canvas_id , mousedown , mouse_button_callback )
2020-07-30 14:08:30 +02:00
SET_EM_WINDOW_CALLBACK ( mousemove , mousemove_callback )
SET_EM_WINDOW_CALLBACK ( mouseup , mouse_button_callback )
2020-10-17 23:31:30 +02:00
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 )
2020-01-17 21:25:14 +01:00
SET_EM_CALLBACK ( EMSCRIPTEN_EVENT_TARGET_DOCUMENT , fullscreenchange , fullscreen_change_callback )
2018-07-08 02:23:19 +02:00
SET_EM_CALLBACK_NOTARGET ( gamepadconnected , gamepad_change_callback )
SET_EM_CALLBACK_NOTARGET ( gamepaddisconnected , gamepad_change_callback )
2020-01-17 21:25:14 +01:00
# undef SET_EM_CALLBACK_NOTARGET
2018-07-08 02:23:19 +02:00
# undef SET_EM_CALLBACK
# undef EM_CHECK
2017-09-03 01:44:00 +02:00
2020-10-17 23:31:30 +02:00
// 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 ,
MainLoop : : NOTIFICATION_WM_MOUSE_ENTER ,
MainLoop : : NOTIFICATION_WM_MOUSE_EXIT ,
MainLoop : : NOTIFICATION_WM_FOCUS_IN ,
MainLoop : : NOTIFICATION_WM_FOCUS_OUT ) ;
godot_js_display_paste_cb ( & OS_JavaScript : : update_clipboard_callback ) ;
godot_js_display_drop_files_cb ( & OS_JavaScript : : drop_files_callback ) ;
2018-07-08 02:23:19 +02:00
visual_server - > init ( ) ;
return OK ;
2014-02-10 02:10:30 +01:00
}
2017-09-03 01:44:00 +02:00
2020-07-27 13:53:03 +02:00
bool OS_JavaScript : : get_swap_ok_cancel ( ) {
return swap_ok_cancel ;
}
2020-01-19 11:44:19 +01:00
void OS_JavaScript : : swap_buffers ( ) {
emscripten_webgl_commit_frame ( ) ;
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : set_main_loop ( MainLoop * p_main_loop ) {
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
main_loop = p_main_loop ;
input - > set_main_loop ( p_main_loop ) ;
}
MainLoop * OS_JavaScript : : get_main_loop ( ) const {
return main_loop ;
}
2020-06-29 18:51:53 +02:00
void OS_JavaScript : : resume_audio ( ) {
if ( audio_driver_javascript ) {
audio_driver_javascript - > resume ( ) ;
}
}
2020-10-17 23:31:30 +02:00
void OS_JavaScript : : fs_sync_callback ( ) {
get_singleton ( ) - > idb_is_syncing = false ;
2020-09-18 19:15:53 +02:00
}
2015-09-12 15:54:47 +02:00
2020-09-18 19:15:53 +02:00
bool OS_JavaScript : : main_loop_iterate ( ) {
2015-09-12 15:54:47 +02:00
2020-09-18 19:15:53 +02:00
if ( is_userfs_persistent ( ) & & idb_needs_sync & & ! idb_is_syncing ) {
idb_is_syncing = true ;
idb_needs_sync = false ;
2020-10-17 23:31:30 +02:00
godot_js_os_fs_sync ( & OS_JavaScript : : fs_sync_callback ) ;
2015-09-12 15:54:47 +02:00
}
2018-08-12 15:39:32 +02:00
2019-01-19 19:39:17 +01:00
if ( emscripten_sample_gamepad_data ( ) = = EMSCRIPTEN_RESULT_SUCCESS )
process_joypads ( ) ;
2017-07-25 20:55:44 +02:00
2018-08-12 15:39:32 +02:00
if ( just_exited_fullscreen ) {
if ( window_maximized ) {
EmscriptenFullscreenStrategy strategy ;
strategy . scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH ;
strategy . canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF ;
strategy . filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT ;
strategy . canvasResizedCallback = NULL ;
2020-10-17 23:31:30 +02:00
emscripten_enter_soft_fullscreen ( canvas_id , & strategy ) ;
2018-08-12 15:39:32 +02:00
} else {
2020-07-30 17:10:16 +02:00
set_window_size ( Size2 ( windowed_size . width , windowed_size . height ) ) ;
2017-07-25 20:55:44 +02:00
}
2018-08-12 15:39:32 +02:00
just_exited_fullscreen = false ;
2017-07-25 20:55:44 +02:00
}
2018-08-12 15:39:32 +02:00
2018-10-02 02:30:27 +02:00
int canvas [ 2 ] ;
2020-10-17 23:31:30 +02:00
emscripten_get_canvas_element_size ( canvas_id , canvas , canvas + 1 ) ;
2018-08-12 15:39:32 +02:00
video_mode . width = canvas [ 0 ] ;
video_mode . height = canvas [ 1 ] ;
if ( ! window_maximized & & ! video_mode . fullscreen & & ! just_exited_fullscreen & & ! entering_fullscreen ) {
windowed_size . width = canvas [ 0 ] ;
windowed_size . height = canvas [ 1 ] ;
}
2014-02-10 02:10:30 +01:00
return Main : : iteration ( ) ;
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : delete_main_loop ( ) {
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
memdelete ( main_loop ) ;
2020-10-17 23:31:30 +02:00
main_loop = NULL ;
2020-01-19 11:44:19 +01:00
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : finalize ( ) {
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
memdelete ( input ) ;
2020-01-19 11:44:19 +01:00
visual_server - > finish ( ) ;
emscripten_webgl_commit_frame ( ) ;
memdelete ( visual_server ) ;
emscripten_webgl_destroy_context ( webgl_ctx ) ;
2020-06-29 18:51:53 +02:00
if ( audio_driver_javascript ) {
memdelete ( audio_driver_javascript ) ;
}
2014-02-10 02:10:30 +01:00
}
2018-07-08 02:23:19 +02:00
// Miscellaneous
2014-02-10 02:10:30 +01:00
2019-05-14 10:28:27 +02:00
Error OS_JavaScript : : execute ( const String & p_path , const List < String > & p_arguments , bool p_blocking , ProcessID * r_child_id , String * r_pipe , int * r_exitcode , bool read_stderr , Mutex * p_pipe_mutex ) {
2018-10-29 21:15:15 +01:00
2020-01-19 11:44:19 +01:00
Array args ;
for ( const List < String > : : Element * E = p_arguments . front ( ) ; E ; E = E - > next ( ) ) {
args . push_back ( E - > get ( ) ) ;
}
String json_args = JSON : : print ( args ) ;
2020-10-17 23:31:30 +02:00
int failed = godot_js_os_execute ( json_args . utf8 ( ) . get_data ( ) ) ;
2020-01-19 11:44:19 +01:00
ERR_FAIL_COND_V_MSG ( failed , ERR_UNAVAILABLE , " OS::execute() must be implemented in Javascript via 'engine.setOnExecute' if required. " ) ;
return OK ;
2018-10-29 21:15:15 +01:00
}
Error OS_JavaScript : : kill ( const ProcessID & p_pid ) {
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( ERR_UNAVAILABLE , " OS::kill() is not available on the HTML5 platform. " ) ;
2018-10-29 21:15:15 +01:00
}
int OS_JavaScript : : get_process_id ( ) const {
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( 0 , " OS::get_process_id() is not available on the HTML5 platform. " ) ;
2018-10-29 21:15:15 +01:00
}
2018-07-08 02:23:19 +02:00
bool OS_JavaScript : : _check_internal_feature_support ( const String & p_feature ) {
2020-12-03 17:15:14 +01:00
if ( p_feature = = " HTML5 " | | p_feature = = " web " ) {
2018-07-08 02:23:19 +02:00
return true ;
2020-12-03 17:15:14 +01:00
}
2018-07-08 02:23:19 +02:00
# ifdef JAVASCRIPT_EVAL_ENABLED
2020-12-03 17:15:14 +01:00
if ( p_feature = = " JavaScript " ) {
return true ;
}
# endif
# ifndef NO_THREADS
if ( p_feature = = " threads " ) {
2018-07-08 02:23:19 +02:00
return true ;
2020-12-03 17:15:14 +01:00
}
# endif
# if WASM_GDNATIVE
if ( p_feature = = " wasm32 " ) {
return true ;
}
2018-07-08 02:23:19 +02:00
# endif
return false ;
2014-02-10 02:10:30 +01:00
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : alert ( const String & p_alert , const String & p_title ) {
2020-10-17 23:31:30 +02:00
godot_js_display_alert ( p_alert . utf8 ( ) . get_data ( ) ) ;
2016-11-18 18:52:44 +01:00
}
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : set_window_title ( const String & p_title ) {
2020-10-17 23:31:30 +02:00
godot_js_display_window_title_set ( p_title . utf8 ( ) . get_data ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2018-09-16 19:37:36 +02:00
void OS_JavaScript : : set_icon ( const Ref < Image > & p_icon ) {
ERR_FAIL_COND ( p_icon . is_null ( ) ) ;
Ref < Image > icon = p_icon ;
if ( icon - > is_compressed ( ) ) {
icon = icon - > duplicate ( ) ;
2019-06-11 14:49:34 +02:00
ERR_FAIL_COND ( icon - > decompress ( ) ! = OK ) ;
2018-09-16 19:37:36 +02:00
}
if ( icon - > get_format ( ) ! = Image : : FORMAT_RGBA8 ) {
if ( icon = = p_icon )
icon = icon - > duplicate ( ) ;
icon - > convert ( Image : : FORMAT_RGBA8 ) ;
}
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 ;
PoolByteArray png ;
size_t len ;
PoolByteArray : : Read r = icon - > get_data ( ) . read ( ) ;
ERR_FAIL_COND ( ! png_image_write_get_memory_size ( png_meta , len , 0 , r . ptr ( ) , 0 , NULL ) ) ;
png . resize ( len ) ;
PoolByteArray : : Write w = png . write ( ) ;
ERR_FAIL_COND ( ! png_image_write_to_memory ( & png_meta , w . ptr ( ) , & len , 0 , r . ptr ( ) , 0 , NULL ) ) ;
w = PoolByteArray : : Write ( ) ;
r = png . read ( ) ;
2020-10-17 23:31:30 +02:00
godot_js_display_window_icon_set ( r . ptr ( ) , len ) ;
2018-09-16 19:37:36 +02:00
}
2016-11-18 18:52:44 +01:00
String OS_JavaScript : : get_executable_path ( ) const {
2017-03-28 03:21:21 +02:00
return OS : : get_executable_path ( ) ;
2016-11-18 18:52:44 +01:00
}
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
Error OS_JavaScript : : shell_open ( String p_uri ) {
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
// Open URI in a new tab, browser will deal with it by protocol.
2020-10-17 23:31:30 +02:00
godot_js_os_shell_open ( p_uri . utf8 ( ) . get_data ( ) ) ;
2018-07-08 02:23:19 +02:00
return OK ;
2015-09-12 15:54:47 +02:00
}
2014-02-10 02:10:30 +01:00
2019-05-20 19:36:24 +02:00
String OS_JavaScript : : get_name ( ) const {
2016-01-21 02:23:15 +01:00
2018-07-08 02:23:19 +02:00
return " HTML5 " ;
}
2016-01-21 02:23:15 +01:00
2018-07-08 02:23:19 +02:00
bool OS_JavaScript : : can_draw ( ) const {
2016-01-21 02:23:15 +01:00
2018-07-08 02:23:19 +02:00
return true ; // Always?
2016-01-21 02:23:15 +01:00
}
2018-07-08 02:23:19 +02:00
String OS_JavaScript : : get_user_data_dir ( ) const {
2016-01-21 02:23:15 +01:00
2018-07-08 02:23:19 +02:00
return " /userfs " ;
} ;
2016-01-21 02:23:15 +01:00
2020-01-19 11:44:19 +01:00
String OS_JavaScript : : get_cache_path ( ) const {
return " /home/web_user/.cache " ;
}
String OS_JavaScript : : get_config_path ( ) const {
return " /home/web_user/.config " ;
}
String OS_JavaScript : : get_data_path ( ) const {
2016-01-21 02:23:15 +01:00
2020-01-19 11:44:19 +01:00
return " /home/web_user/.local/share " ;
2016-01-21 02:23:15 +01:00
}
2017-09-12 21:09:06 +02:00
OS : : PowerState OS_JavaScript : : get_power_state ( ) {
2018-03-21 15:51:44 +01:00
2020-01-19 11:44:19 +01:00
WARN_PRINT_ONCE ( " Power management is not supported for the HTML5 platform, defaulting to POWERSTATE_UNKNOWN " ) ;
2018-03-21 15:51:44 +01:00
return OS : : POWERSTATE_UNKNOWN ;
2016-07-23 13:15:55 +02:00
}
int OS_JavaScript : : get_power_seconds_left ( ) {
2018-03-21 15:51:44 +01:00
2020-01-19 11:44:19 +01:00
WARN_PRINT_ONCE ( " Power management is not supported for the HTML5 platform, defaulting to -1 " ) ;
2018-03-21 15:51:44 +01:00
return - 1 ;
2016-07-23 13:15:55 +02:00
}
2014-02-10 02:10:30 +01:00
2016-07-23 13:15:55 +02:00
int OS_JavaScript : : get_power_percent_left ( ) {
2018-03-21 15:51:44 +01:00
2020-01-19 11:44:19 +01:00
WARN_PRINT_ONCE ( " Power management is not supported for the HTML5 platform, defaulting to -1 " ) ;
2018-03-21 15:51:44 +01:00
return - 1 ;
2016-07-23 13:15:55 +02:00
}
2018-07-08 02:23:19 +02:00
void OS_JavaScript : : file_access_close_callback ( const String & p_file , int p_flags ) {
2018-01-12 00:15:21 +01:00
2018-07-08 02:23:19 +02:00
OS_JavaScript * os = get_singleton ( ) ;
2020-09-18 19:15:53 +02:00
if ( ! ( os - > is_userfs_persistent ( ) & & p_flags & FileAccess : : WRITE ) ) {
return ; // FS persistence is not working or we are not writing.
}
bool is_file_persistent = p_file . begins_with ( " /userfs " ) ;
# ifdef TOOLS_ENABLED
// Hack for editor persistence (can we track).
is_file_persistent = is_file_persistent | | p_file . begins_with ( " /home/web_user/ " ) ;
# endif
if ( is_file_persistent ) {
os - > idb_needs_sync = true ;
2018-07-08 02:23:19 +02:00
}
}
2018-01-12 00:15:21 +01:00
2018-07-08 02:23:19 +02:00
bool OS_JavaScript : : is_userfs_persistent ( ) const {
2017-10-02 16:09:24 +02:00
2018-07-08 02:23:19 +02:00
return idb_available ;
2017-10-02 16:09:24 +02:00
}
2020-12-05 00:43:02 +01:00
Error OS_JavaScript : : open_dynamic_library ( const String p_path , void * & p_library_handle , bool p_also_set_library_path ) {
String path = p_path . get_file ( ) ;
p_library_handle = dlopen ( path . utf8 ( ) . get_data ( ) , RTLD_NOW ) ;
ERR_FAIL_COND_V_MSG ( ! p_library_handle , ERR_CANT_OPEN , " Can't open dynamic library: " + p_path + " . Error: " + dlerror ( ) ) ;
return OK ;
}
2018-07-08 02:23:19 +02:00
OS_JavaScript * OS_JavaScript : : get_singleton ( ) {
2017-10-02 16:09:24 +02:00
2018-07-08 02:23:19 +02:00
return static_cast < OS_JavaScript * > ( OS : : get_singleton ( ) ) ;
2017-10-02 16:09:24 +02:00
}
2020-10-17 23:31:30 +02:00
OS_JavaScript : : OS_JavaScript ( ) {
// Expose method for requesting quit.
godot_js_os_request_quit_cb ( & request_quit_callback ) ;
// Set canvas ID
godot_js_config_canvas_id_get ( canvas_id , sizeof ( canvas_id ) ) ;
2018-07-08 02:23:19 +02:00
2020-10-17 23:31:30 +02:00
cursor_inside_canvas = true ;
cursor_shape = OS : : CURSOR_ARROW ;
2018-01-07 00:04:09 +01:00
2018-07-20 18:16:09 +02:00
last_click_button_index = - 1 ;
last_click_ms = 0 ;
last_click_pos = Point2 ( - 100 , - 100 ) ;
2020-06-29 18:54:20 +02:00
last_width = 0 ;
last_height = 0 ;
2017-03-05 16:44:50 +01:00
window_maximized = false ;
2018-08-12 15:39:32 +02:00
entering_fullscreen = false ;
just_exited_fullscreen = false ;
2020-02-07 01:18:11 +01:00
transparency_enabled = false ;
2014-02-10 02:10:30 +01:00
2018-07-08 02:23:19 +02:00
main_loop = NULL ;
2020-01-19 11:44:19 +01:00
visual_server = NULL ;
2020-06-29 18:51:53 +02:00
audio_driver_javascript = NULL ;
2018-07-08 02:23:19 +02:00
2020-07-27 13:53:03 +02:00
swap_ok_cancel = false ;
2020-10-17 23:31:30 +02:00
idb_available = godot_js_os_fs_is_persistent ( ) ! = 0 ;
2020-09-18 19:15:53 +02:00
idb_needs_sync = false ;
idb_is_syncing = false ;
2015-09-12 15:54:47 +02:00
2020-06-29 18:51:53 +02:00
if ( AudioDriverJavaScript : : is_available ( ) ) {
audio_driver_javascript = memnew ( AudioDriverJavaScript ) ;
AudioDriverManager : : add_driver ( audio_driver_javascript ) ;
}
2018-01-07 00:04:09 +01:00
Vector < Logger * > loggers ;
loggers . push_back ( memnew ( StdLogger ) ) ;
_set_logger ( memnew ( CompositeLogger ( loggers ) ) ) ;
2018-03-04 18:18:05 +01:00
2018-07-08 02:23:19 +02:00
FileAccessUnix : : close_notification_func = file_access_close_callback ;
2014-02-10 02:10:30 +01:00
}